iOS 9 Storyboard 教程(一下)

接iOS 9 Storyboard 教程(一上)

上篇鏈接

原型cell

你可以直接從storyboard編輯器中,使用原型cell你可以很容易的為你的tableViewCell設計一套自定義的布局.

現在的Table View Controller有一個空的原型cell.點擊原型cell,你可以在Attributes inspector中設置它的樣式(Style)和副標題(Subtitle).

在storyboard中有很多可堆疊的內容,但有時卻很難點擊你想確切操作的內容.如果你遇到麻煩,下面有幾個選項可以幫你.第一個就是在左側的Document Outline里,你可以選擇這個item.第二個是一個方便的熱鍵:按住control+shift并點擊你感興趣區域.會出現一個彈出框讓你直接使用光標選擇任何元素.

如果你之前使用過table view,兵器手動創建過cell,你可能會認出這是UITableViewCellStyle.帶副標題(Subtitle)的樣式.和原型cell一樣,你也可以選擇一個內置的cell樣式,就好像你剛才做的一樣,或者創建一個自定義設計(你很快機會這樣做).

設置Accessory屬性為Disclosure Indicator然后把Identifier屬性設置PlayerCell.所有的原型cell都應該有一個可重用的標識符(identifier),這樣你才能在代碼里引用它們.

運行app,但是好像卻沒有任何改變…并不是很奇怪:你還必須為tableView添加數據源,這樣它才會知道應該顯示多少行數據.這正是你接下來要做的.

在工程中添加一個新文件.然后在 iOS/Source選項中,選擇Cocoa Touch Class模板.給這個類命名為PlayersViewController并且把它設置為UITableViewController的子類.不選Also create XIB file.

選擇Swift語言,然后點擊下一步(Next)創建.

回到storyboard然后選中Table View Controller(確保你選的是實際的視圖控制器而不是它里面的某一個視圖).在Identity inspector里,設置它的Class是PlayersViewController.對于把剛才創建的類連接到storyboard里的自定義view controller,這是至關重要的一步.不要忘記這一步,否則你剛創建的類將不能使用!

從現在開始,當你運行app的時候,storyboard 中的table view controller就變成了PlayersViewController類的一個實例.
這個table view應該會顯示一列玩家名單,所以現在你需要為這個app創建一個數據模型—一個包含Player對象的數組.使用Swift File模板在iOS/Source里為這個工程添加一個新文件.命名為Player. 替換Player.swift中代碼:

import UIKitstruct Player { 
  var name: String? 
  var game: String? 
  var rating: Int init(name: String?, game: String?, rating: Int) { 
  self.name = name 
  self.game = game 
  self.rating = rating 
  }
}

這里沒有發生什么特別的事.Player類是一個簡單的容器對象,包含有三個屬性:玩家的姓名(name),他們正在玩的游戲(game)以及一個額定1至5星的評級(rating).

接下來,你需要把一個Player對象數組賦值給PlayersViewController.使用Swift File模板為開始,創建一個新文件,命名為SampleData.把它添加到SampleData.swift的末尾.

//Set up sample datalet 
playersData = [ 
  Player(name:"Bill Evans", game:"Tic-Tac-Toe", rating: 4),   
  Player(name: "Oscar Peterson", game: "Spin the Bottle", rating: 5), 
  Player(name: "Dave Brubeck", game: "Texas Hold 'em Poker", rating: 2) ]

現在你已經定義了一個叫做playersData的常量,并且分配了一個硬編碼的Player對象數組給它.

現在在PlayersViewController.swift文件里的class PlayersTableViewController: UITableViewController下面添加一個Player 數組:

var players:[Player] = playersData

當定義players變量時,你可以很容易在PlayersViewController里設置樣本數據.但由于這些數據在后面也許會從一個plist文件或者SQL文件中取,所以在視圖控制器外部加載數據是很明智的.

現在你有一個包含很多Player對象的數組.你可以繼續在PlayersViewController里鏈接數據源.用下面的方法替換table view的數據源:

override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 
  return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
  return players.count
}

真正的工作發生在cellForRowAtIndexPath.使用下面的代碼替換原來的方法:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
  let cell = tableView.dequeueReusableCellWithIdentifier("PlayerCell", forIndexPath: indexPath) 
  let player = players[indexPath.row] as Player 
  cell.textLabel?.text = player.name 
  cell.detailTextLabel?.text = player.game return cell
}

這個方法dequeueReusableCellWithIdentifier(_:forIndexPath:) 將會檢查是否有可用于回收的cell.如果沒有,它將自動分配一個原型cell并把它返回給你.你所需要做的就是提供可重用的標識符,你可以在storyboard編輯器里設置原型cell — 在這種情況下的PlayerCell.不要忘記設置標識符,否則這個小的方案將不會起作用! 運行app,看啊,table view上有玩家了!

只需要幾行代碼就可以這些原型cell.我覺得那太棒了!


Note:

在這個app,你只用到了一個原型cell,但如果你的table需要顯示不同的cell,那么你可以很簡單的在storyboard中添加額外的原型cell.你也可以復制已經存在的cell,使它成為一個新的cell,或者增加Table View原型cell的值屬性.確保給予了每一個cell屬于它自己的標識符.


設計你自己的原型cell

對于很多app來說,使用一個標準的cell樣式也是可以的,但是對于這個app來說,如果你想要在cell的右手邊添加一張玩家評級(1–5星)的圖片.那就需要有一個(圖片視圖)image view,就目前來看,標準的cell樣式是不支持的,所以你必須要自定義設計一個.

切換回Main.storyboard,在 table view,里選擇原型cell,并且在Attributes inspector中,設置Style屬性為自定義(Custom).現在默認標簽已經消失了.

先使cell在高一點.也可以在Size inspector(之后選擇自定義)改變Row Height 的值.或者拖動cell的底部,設置高度60.

從Objects Library 拖拽兩個Label對象到cell里,把它們粗略的放到標準標簽的位置.只要在Attributes Inspector選擇你喜歡字體和顏色.設置頂部標簽為Name,底部標間為Game.

在Document Outline里同時選中Name和Game標簽,然后按住Command+點擊,選擇Editor\Embed In\Stack View.


Note:

堆棧視圖(Stack view)是iOS9新加入的,它可以很容易的布局視圖的集合樣式.


拖拽一個ImageView到cell并把它放到右側,在Size Inspector里設置它寬為81高為35.設置它的Mode在中心(Center)(在Attributes inspector下一個),這樣無論你把這張圖片放置到view的任何地方,它都是不伸展的.

在Document Outline里Command + 點擊Stack View和Image View.選擇Editor\Embed in\Stack View.Xcode將會創建一個新的水平 stack view 包含這兩個控件.

選中新的水平stack view,在Attributes Inspector里改變Alignment為Centre 并且Distribution改為Equal Spacing. 現在對于這個控制器來說,包含了一些簡單地自動布局.在storyboard的右側底部點擊Pin圖標:

改變約束為Top: 0, Right: 20, Bottom: 0 and Left: 20.確保這四個紅色指針在圖片中高亮顯示.點擊彈出窗口底部的 Add 4 Constraints.

如果你的stack view有橙色的約束,表明它錯位了.為了解決這個問題,選擇水平的stack view然后選擇Editor\Resolve Auto Layout Issues\Update Frames(在選中的菜單視圖部分).這個stack view應該放到正確的位置上,之后橙色的約束錯誤就會消失了.
為了把image view within 放到 stack view里,在Document Outline里選中image view,然后選擇Editor\Resolve Auto Layout Issues\Add Missing Constraints(在選中的菜單視圖部分). 最終的為原型cell設計的樣子看上去向下面的一樣:

因為這是一個自定義設計的cell,你不能在把UITableViewCell的 textLabel 和 detailTextLabel屬性放到標簽里了.這些屬性所指的標簽不再是這個cell里的了;它們只在標準的cell類型里才合法.取而代之的是,你需要使用tag來找到這些標簽.
tag被用在這里更加簡單.在后面的課程里,你會創建一個自定義的類,繼承自UITableViewCell,并且包含對應于你的cell視圖的屬性. 在Attributes inspector中,設置Name標簽的tag值為100,Game標簽的tag值為101,以及Image View的tag值為102.
然后打開PlayersViewController.swift,在這個類的底部,添加一個新方法叫做imageForRating.如下代碼:

func imageForRating(rating:Int) -> UIImage? { 
  let imageName = "\(rating)Stars" 
  return UIImage(named: imageName)
}

相當簡單—根據評級顯示不同的星的圖標.仍然在PlayersViewController里改變tableView(_:cellForRowAtIndexPath:),如下:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("PlayerCell", forIndexPath: indexPath) //1 
  let player = players[indexPath.row] as Player //2 
  if let nameLabel = cell.viewWithTag(100) as? UILabel { //3
  nameLabel.text = player.name 
  } 
  if let gameLabel = cell.viewWithTag(101) as? UILabel {
   gameLabel.text = player.game 
  } 
  if let ratingImageView = cell.viewWithTag(102) as? UIImageView { 
  ratingImageView.image = self.imageForRating(player.rating) 
  } 
  return cell
}

你所做的會出現崩潰:

1.dequeueReusableCellWithIdentifier將會使用重用標識符PlayerCell,重用已經存在的cell如果不存在就創建一個新的.

2.你查找每一行對應的Player對象,并將其分配給player.

3.可以看到標簽和圖片的數據都來自player對象.

應該這樣做.現在再一次運行app,他看上去好像下面這樣:


恩…,那看起來不太對—cell顯示的好像被壓扁了一些.你確實改變了原型cell的高度,但 table view 卻不這么認為.有兩個方法解決這個問題:你可以改變 table view的高度屬性,或者實現tableView(tableView:heightForRowAtIndexPath:) 方法.前者是更適用這種情況,因為我們只擁有一種類型的cell,并且我們事先知道cell的高度.


Note:

如果你事先不知道你的cell的高度,或者不同的cell有著不同的高度,你將會使用 tableView(tableView:heightForRowAtIndexPath:).


返回Main.storyboard,在Table View的Size inspector里,設置高度為60.


如果你現在運行app,它看起來好多了!

順便說一句,如果你通過拖拽改變了cell的高度,而沒有改變它的值,那么table view的行高屬性也會自動改變.所以在第一次可能是正確的.

使用cell的子類

Table view已經非常好了,但是我不是使用tag來訪問標簽和其他cell子視圖的粉絲.如果你能通過連線(outlet)連接這些標簽(label),然后使用對應的屬性那么它將更干凈.事實證明,你可以. 在工程中添加一個新的文件,使用Cocoa Touch Class模板.命名它為PlayerCell,并且把它作為UITableViewCell的子類.不要勾選創建XIB的選項,正如你在storyboard中已經有的cell一樣. 添加PlayerCell類的屬性,就好像下面的類定義一樣:

@IBOutlet weak var gameLabel: UILabel!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var ratingImageView: UIImageView!

所有的變量都是IBOutlet類型的,它可以連接到當前storyboard中的控制器. 使用IBOutlet就好像下面這樣添加屬性:

var player: Player! { 
  didSet { 
    gameLabel.text = player.game 
    nameLabel.text = player.name 
    ratingImageView.image = imageForRating(player.rating) 
  }
}

無論當什么時候設置了player的屬性,它都會正確地更新IBOutlet里的信息.

把imageForRating(_:)方法從PlayersViewController移動到PlayerCell類里,這樣可以在一個類里保持cell的詳細信息. 返回Main.storyboard,選中PlayerCell然后在Identity inspector里改變它的類為PlayerCell.

現在無論什么時候你只要改變table view數據源的dequeueReusableCellWithIdentifier(_:forIndexPath:)方法,它都會返回一個PlayerCell實例而不是返回UITableViewCell. 現在你已經給了這個類和重用標識符相同的名字 — 它們都被叫做PlayerCell — 但那只是因為我想要讓它們保持一致.類名和重用標識符彼此無關,所以你也可以給它們命名成不同的名字,如果你想那么做的話.

現在將標簽和圖片視圖都連到這些outlet上.在storyboard中導航到Connections Inspector,然后從Document Outline或者工作空間里選擇PlayerCell.在Connections inspector中拖拽nameLabel.

給Document Outline中的Name標簽對象,或者是拖拽到工作空間中.重復gameLabel和ratingImageView.


Important:

你應該把控件連接到table view cell上,而不是連接到view controller!你看,只要你的數據源請求table view通過dequeueReusableCellWithIdentifier創建了一個新的cell,那么這個table view將不會調用真正的原型cell,而是一個拷貝(或者如果可能的話,之前有一個cell是可回收的).

這就意味著,在任何給定的時間都有超過一個實例.如果你是將一個標簽從cell連接到控制器,那么幾個標簽的副本將會嘗試使用相同的連線.這只是要求麻煩.(在另一方面,將原型cell的活動連接到視圖控制器上的動作是非常好的.如果在你的cell上有自定義的按鈕或者其他控件,你將會這么做的)


現在你已經連接了這些屬性,你可以簡化數據源代碼.在PlayersViewController,改變tableView(_:cellForRowAtIndexPath:)如下:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
  let cell = tableView.dequeueReusableCellWithIdentifier("PlayerCell", forIndexPath: indexPath) as! PlayerCell 
  let player = players[indexPath.row] as Player cell.player = player return cell
}

更像是這樣.你可以從dequeueReusableCellWithIdentifier這個方法里得到一個PlayerCell的對象,然后你就可以簡單地把正確的玩家信息傳遞到cell上.在PlayerCell里設置玩家變量將會自動地把值傳遞到標簽和圖片視圖上,并且cell會使用你在storyboard里的連線.難道使用原型cell使table view變得很整潔不好么?

運行app并且嘗試做一下.它依舊會和之前一樣,但是在屏幕下面,它使用的是你自己的table view cell的子類!你可以在這里下載所有的源代碼.

結語

如果你有任何問題都可以在下面討論,翻譯過程中有個別地方翻譯不是十分準確,希望大家批評指正后面會繼續更新第二部分,敬請期待!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,936評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,744評論 3 421
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,879評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,181評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,935評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,325評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,384評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,534評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,084評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,892評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,623評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,322評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,735評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,990評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,800評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,084評論 2 375

推薦閱讀更多精彩內容