Swift Swift語言Storyboard教程:第二部

本文由CocoaChina翻譯小組@TurtleFromMars翻譯自raywenderlich,原文:Storyboards Tutorial in Swift: Part 2swift

更新記錄:該Storyboard教程由Caroline Begbie更新iOS 8和Swift相關內容。原文做者爲教程編纂組的成員Matthijs Hollemans數組

2014/12/5更新:更新至 Xcode 6.2 Beta。app

若是你想學習Storyboard,你來對地方了!編輯器

本系列Storyboard教程的第一部分,咱們已經學習瞭如何使用Interface Builder建立並鏈接不一樣的視圖控制器,還有如何直接在Storyboard編輯器中建立自定義表項。ide

本教程的第二部分,也是最終部分,內容包括segue(轉場),static table view cell(靜態表項),添加玩家頁面和遊戲選擇頁面!函數

咱們從上部分結束的地方開始,請先打開以前的項目,或者下載上半部分教程的示例代碼工具

好,如今讓咱們一塊兒探索Storyboard的其餘酷炫特性吧!佈局

轉場(Segue)性能

讓咱們向Storyboard中繼續添加視圖控制器,建立一個讓用戶添加新玩家的頁面。學習

打開Main.storyboard,在包含表視圖的那個Players場景的導航欄右側拖入一個Bar Button Item(欄按鈕項),在屬性檢查器中將Identifier設爲Add,使其成爲標準添加(加號)按鈕。

01.png

當用戶點按這個按鈕時,你但願App會彈出一個模態頁面讓用戶輸入新玩家的詳細信息。

在Players場景的右邊拖入一個新的Navigation Controller(導航控制器)。記得雙擊面板能夠縮放畫面騰出空間。新加入的導航控制器附帶一個表視圖控制器,很方便。

這裏有個小技巧:選擇剛纔在Players頁面里加入的加號按鈕,按住control鍵把它拖向新建的導航控制器,鬆手,在彈出的小選單中選擇modal(模態)。

還記得嗎:當Storyboard面板處於縮小狀態時,沒法添加或修改內容。若是在建立轉場時遇到問題,請嘗試雙擊放大!

02.gif

如今Players頁面和導航控制器之間多了一個新箭頭。

03.png

這 種鏈接的類型叫作segue(轉場,讀做seg-way,源自電影術語,原指兩個場景間的過渡銜接),表示一個頁面到另外一個頁面的過渡。此前咱們所見的 Storyboard鏈接描述的都是視圖控制器的包含關係,而轉場是用來切換頁面的。轉場能夠由點擊按鈕、表項、手勢等條件觸發。

使用轉場的好處是,不再用爲呈現新頁面寫代碼了,也不用把按鈕鏈接到IBAction方法上,你只須要在Storyboard中從一個欄按鈕項拖到下一個頁面就能夠建立過渡了。(注:若是你的控件已經綁定了IBAction鏈接,該鏈接會被轉場屏蔽。)

運行App,點擊加號按鈕,一個新的表視圖會從屏幕下方滑入。

04.gif

這就是所謂的模態轉場。新頁面徹底覆蓋原頁面,在關閉模態頁面以前,用戶只能在新頁面進行交互。後面咱們還會看到push(入棧)轉場,這種轉場會把新頁面壓入導航控制器的導航棧(navigation stack)。

如今新頁面還沒什麼用,連關閉頁面返回都作不到,有去無回,由於轉場是單向操做。

爲返回頁面,Storyboard提供了unwind(回退)轉場。接下來咱們要實現返回功能,主要分三個步驟:

1.  建立讓用戶點選的控件,一般是個按鈕。

2.  在你想返回的控制器建立回退方法。

3.  在Storyboard中將控件與回退方法鏈接。

首 先打開Main.storyboard,選擇新的表視圖控制器場景(叫「Root View Controller」的那個)。雙擊導航欄,把標題改爲「Add Player」。而後在導航欄添加兩個欄按鈕項,在屬性檢查器中設置左側按鈕的Identifier爲Cancel,右側按鈕爲Done,並將右側按鈕的 Style改爲Done。

05.png

接 下來在項目中用Cocoa Touch Class模板添加一個新文件,命名爲PlayerDetailsViewController並令其繼承 `UITableViewController`。要把這個類關聯到Storyboard,先切回Main.storyboard,選擇添加玩家的場景, 而後在身份檢查器(Identity inspector)中設Class爲PlayerDetailsViewController。這個步驟我常常忘掉,在此特意提醒,還請讀者牢記。

如今終於能夠建立回退轉場了。在PlayersViewController.swift(不是detail那個)的類定義下面添加以下的回退方法:

@IBAction func cancelToPlayersViewController(segue:UIStoryboardSegue) {
  dismissViewControllerAnimated(true, completion: nil)
}
 
@IBAction func savePlayerDetail(segue:UIStoryboardSegue) {
  dismissViewControllerAnimated(true, completion: nil)
}

這兩個方法在調用時都會解除這個控制器。後面你會改寫`savePlayerDetail`,讓它名副其實地履行本身的職責。

最後回到Interface Builder,把Cancel按鈕和Done按鈕鏈接到相應的action方法上。按住control從欄按鈕拖到視圖控制器上面的出口(exit)對象上,而後從彈出的選單中選擇正確的action名稱。

06_sb2_connectdone2.gif

記住取消方法的方法名,建立回退轉場時,App中的全部回退方法(形如`@IBAction func methodname(segue:UIStoryboardSegue)`)都會在列表中顯示,因此命名方法時要多加註意,避免混淆。

運行App,點擊加號按鈕,而後測試Cancel和Done按鈕。僅僅幾行代碼就能夠實現如此功能。

靜態表項(Static Cell)

完成這部分後,添加玩家頁面會像這樣:

07.jpg

當 然這是一個分組表視圖(grouped table view),但沒必要爲該表建立數據源,也沒必要爲此編寫`cellForRowAtIndexPath`方法,你能夠直接在Interface Builder中完成設計。這個特性叫作靜態表項(static cell)。

選中Add Player場景的表視圖,在屬性檢查器中設Content爲Static Cells,把Style由Plain改爲Grouped,併爲表視圖設置兩個分段(section)。

08.png

修改Sections屬性值時,編輯器會複製已有的分段。(你也能夠在左側的文檔大綱中選擇特定分段並複製。)

最終頁面每一個分段應該只有一行,請在面板或文檔大綱中選中並刪除多餘的表項。

在文檔大綱中選擇最上面的表視圖分段,在屬性選擇器中設Header字段值爲Player Name。

11_sb2_staticcellsheader.png

向該分段內拖入一個新的Text Field(文本字段),橫向拉長並移除邊框,使文本字段控件融入周圍環境。設字體爲 _System 17.0_ ,勾掉Adjust to Fit選項。

textField-on-cell-480x253.png

接 下來咱們要用Xcode的Assistant Editor(輔助編輯器)功能爲該文本字段在`PlayerDetailsViewController`中建立一個outlet。在 Storyboard中,點擊工具欄上的按鈕(圖標是兩個套在一塊兒的圓圈)打開輔助編輯器,應該會自動打開 PlayerDetailsViewController.swift(若是沒有,在右側的跳轉欄中選擇相應文件)。

選擇新建的文本字段, 按住control拖到swift文件的類定義下面。在彈出框中將新outlet命名爲nameTextField並點擊Connect。在點擊 Connect後Xcode會在PlayersDetailViewController類中添加屬性並在Storyboard中創建鏈接:

12_sb2_dragoutlet.gif

爲表項上的視圖建立outlet對於原型表項來講可能會遇到問題,這在上一部分的教程中提到過,不過靜態表項就沒必要擔憂了,由於每一個靜態表項都只會有惟一的實例,把子視圖與視圖控制器的outlet鏈接徹底沒問題。

把第二分段的靜態表項的Style設爲Right Detail,這會套用一個標準表項樣式,雙擊左側的label,把文本改成Game,而後爲該表項設定Disclosure Indicator(展開方向標)附件。

12_sb2_gamecell.png

仿 照剛纔的Name文本字段,爲右面的label("Detail"的那個)建立outlet並命名爲detailLabel,該表項上的label都是常 規`UILabel`對象。在創建鏈接前選擇Detail文本字段時可能須要屢次點擊,請確保選擇的是label而不是整個表項。完成後如圖:

13_sb2_selecdetaillabel-700x223.png

添加玩家頁面的最終設計效果如圖:

14_sb2_staticcellsfinal.png

目前在Storyboard中設計的頁面尺寸都符合iPhone 5的4英寸屏幕,高度爲568點。固然你的App應當在不一樣的屏幕尺寸下正常工做,你能夠在Storyboard中預覽全部的尺寸。

在工具欄上點開輔助編輯器,選擇跳轉欄中的Preview。點擊輔助編輯器左下角的加號添加新的預覽尺寸,若是想刪除一個屏幕尺寸,選中並按delete鍵便可。

15_screen_preview-405x500.png

一個簡單的評分App不須要什麼花哨的東西,只是使用表視圖控制器,頁面自動縮放以填滿屏幕空間。當你想爲不一樣的屏幕尺寸適配佈局時,你須要使用Auto Layout和Size Classes。

構建並運行App,你會注意到添加玩家頁面依然是空白!

16.png

表視圖控制器在使用靜態表項時不須要數據源,而以前你用Xcode模板建立的`PlayerDetailsViewController`類中依然有部分數據源相關代碼,靜態表項所以沒法正常工做,因此靜態內容沒有顯示出來。咱們這就來解決問題!

打開PlayerDetailsViewController.swift文件,刪除這一條代碼往下的全部內容(注意不要刪掉類本身的括號):

// MARK: - Table view data source

如今,自從加入這個類之後Xcode顯示的那幾條警告(warning)也應該消失了。

運行App,檢查使用靜態表項的新頁面。徹底沒有寫代碼,其實剛纔還刪了一段代碼!

還 要了解一點:靜態表項只在`UITableViewController`中有效,雖然Interface Builder容許你在常規`UIViewController`中的表視圖對象裏添加靜態表項,運行時不會發揮做用,緣由是 `UITableViewController`中額外實現了一些用來處理靜態表項數據源的操做。在項目中誤用的話Xcode甚至會拒絕編譯,輸出報錯信 息:「Illegal Configuration: Static table views are only valid when embedded in UITableViewController instances」。

另外一方面,原型表項在常規視圖內的表視圖中能夠正常工做,但在nib中就沒戲了。目前來說,使用原型表項或靜態表項就必須使用Storyboard。

你也有可能想在一個表視圖中混合使用靜態表項和常規的動態表項,很遺憾的是目前的SDK對此支持欠佳。若是你的App有這種需求,請參考蘋果開發者官方論壇上的相關帖子尋求可行方案。

注:若是構建的頁面上包含的靜態表項多到沒法在可視範圍內所有展現,你能夠在Interface Builder中直接利用滾動手勢查看,這個功能可能不容易發現,但確實管用。

不過總的來講該寫代碼的地方只能靠代碼,甚至靜態表項的表視圖也是如此。前面在把文本字段拖進第一個表項的時候,你可能發現尺寸不大合適,文本字段周圍有一點白邊,並且用戶看不到文本字段的實際範圍,若是正好點在邊框上,沒有彈出鍵盤,用戶會感到困惑。

爲避免這種狀況,你應該讓那一行任意位置接受的點擊均可以喚出鍵盤。要這樣作很容易,打開PlayerDetailsViewController.swift並以下添加

tableView(_:didSelectRowAtIndexPath:)`方法:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
  if indexPath.section == 0 {
    nameTextField.becomeFirstResponder()
  }
}

代碼的意思是若是用戶點按第一個表項,App應該激活相應文本字段。該分段只有一個表項,你只需使用分段的索引。設文本字段爲第一響應者會自動喚出鍵盤。這只是一小處用戶體驗優化,但就是這樣一個小細節能夠給用戶省去一點煩惱。

小訣竅:添加delegate委託方法或重寫視圖控制器方法時,直接輸入方法名開頭的幾個字母(前面不加func),便可在自動補全列表中選擇正確的方法。

另外,還應該在Storyboard的屬性檢查器中把相應表項的Selection Style設爲None(本來是Default),不然用戶點按文本字段周圍的邊框時該行會高亮。

16_sb2_selectionstyle-700x314.png

好啦,添加玩家頁面設計完成。如今咱們要實現功能。

爲添加玩家頁面實現功能

如今先無論Game這行,只輸入玩家名稱。

當用戶點擊Cancel按鈕時,頁面關閉,用戶剛剛輸入的數據隨之做廢。這部分功能直接用回退轉場已經實現好了。

而當用戶點擊Done時,你應該建立一個新的Player對象,參照用戶輸入填充屬性後更新玩家列表。

轉場即將發生時,`prepareForSegue(:sender:)`會被調用。你能夠重寫這個方法,在退出視圖以前將數據保存到一個新的Player對象中。

注:不要擅自調用`prepareForSegue`方法,這是UIKit通知你一個轉場剛剛被觸發的消息。

在PlayerDetailsViewController.swift中,先在類上添加一條屬性:

var player:Player!

這條語句並不會將屬性實例化,但其中的感嘆號把該變量定義爲隱式解包可選量(implicitly unwrapped optional),意思是該變量必須被實例化,並且你肯定它在被使用前必定有值。

接下來在PlayerDetailsViewController.swift中添加如下方法:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  if segue.identifier == "SavePlayerDetail" {
    player = Player(name: self.nameTextField.text, game: "Chess", rating: 1)
  }
}

prepareForSegue(_:sender:)`方法判斷轉場的標識符是否爲`SavePlayerDetail`,當且僅 當斷定結果爲真時,建立一個新的Player實例,其中game和rating均取默認值。若是此時運行,App會崩潰,由於不存在標識符 `SavePlayerDetail`,player不會被實例化,結合前面的隱式解包可選量定義,引起運行時錯誤。

小提示:若是App出現詭異的崩潰問題,並且代碼看起來彷佛並沒有邏輯錯誤,那麼多是在代碼中刪除過對象或修改過對象名,以至Storyboard引用對象出錯。

在Main.storyboard中,在文檔大綱裏找到Add Player場景,選擇鏈接到`savePlayerDetail`這個action的回退轉場,將其標識符改成`SavePlayerDetail`:

16_sb2_unwind_identifier-700x360.png

而後選擇鏈接到`cancelToPlayersViewController`的回退轉場,將其標識符改成`CancelPlayerDetail`。以供`prepareForSegue(_:sender:)`方法判斷標識符。

轉到PlayersViewController類,以下修改回退轉場方法`savePlayerDetail(segue:)`:

@IBAction func savePlayerDetail(segue:UIStoryboardSegue) {
  let playerDetailsViewController = segue.sourceViewController as PlayerDetailsViewController
 
  //add the new player to the players array
  players.append(playerDetailsViewController.player)
 
  //update the tableView
  let indexPath = NSIndexPath(forRow: players.count-1, inSection: 0)
  tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
 
  //hide the detail view controller
  dismissViewControllerAnimated(true, completion: nil)
}

這會經過傳入方法的轉場引用獲取一個指向`PlayerDetailsViewController`的引用,並藉此向數據源中使用的Player數組添加新的Player對象,而後通知表視圖在末尾新增了一行,由於表視圖和數據源應當保持同步。

你可能會直接調用`tableView.reloadData()`,但仍是爲新行插入的操做加入動畫效果比較好。`UITableViewRowAnimation.Automatic`會以插入新行的位置自動選用合適的動畫,十分方便。

試試看,如今應該能夠向列表中加入新玩家了!

App-with-new-players-273x500.png

性能

現 在Storyboard已經有好幾個視圖控制器了,你或許會擔憂性能問題,不過一次載入整個Storyboard並非什麼苦活,Storyboard不 會當即實例化全部的視圖控制器,當即載入的只有初始視圖控制器。而因爲這裏的初始視圖控制器是一個分頁欄控制器,包含的兩個視圖控制器也會被載入(第一個 分頁標籤的Players場景和第二個分頁標籤的場景)。

其餘視圖控制器只有在轉場過去的時候纔會被實例化。而當關閉視圖控制器的時候,它們會當即被釋放,因此內存中只有活躍使用的視圖控制器,就好像分別使用nib同樣。

實踐是檢驗真理的惟一標準,在PlayerDetailsViewController類中添加構造器(initializer)和析構器(deinitializer):

required init(coder aDecoder: NSCoder) {
  println("init PlayerDetailsViewController")
  super.init(coder: aDecoder)
}
 
deinit {
  println("deinit PlayerDetailsViewController")
}

你剛剛重寫了`init(coder:)`和`deinit`方法,讓它們向Xcode調試面板輸出信息。如今運行App,打開添加玩家頁面,你會發現視圖控制器只有在被打開的時候纔會分配。

關閉添加玩家頁面的時候,不管是點擊Cancel仍是Done都會看到deinit析構器的`println()`輸出。若是再次打開這個頁面,你還會看到`init(coder:)`的輸出,這樣你應該相信這個事實了:視圖控制器是按需加載的,就像手動載入nib同樣。

注: 若是你之前用過nib,那麼你應該會很熟悉構造器`init(coder:)`,這部分機制延續到了Storyboard中:使用的方法依然是 `init(coder:)`,`awakeFromNib()`和`viewDidLoad()`。Storyboard能夠當作附帶了過渡信息和關聯 信息的一系列nib的集合,而Storyboard內的視圖和視圖控制器使用與nib相同的方式編碼並解析。

遊戲選擇頁面

在添加玩家頁面中點選Game行應該打開一個新頁面並讓用戶從列表中選擇一個遊戲,這意味着下一步要加入另一個表視圖控制器,不過此次的頁面不是模態顯示,而是壓入導航棧。

向 Storyboard中拖入一個新的表視圖控制器,在添加玩家頁面中選擇Game表項(確保選中的是整個表項,而不是其中的label),而後按住 control拖到新建的表視圖控制器,在二者之間建立轉場。在彈出的選單中選擇轉場類型爲Push,而後在屬性檢查器中把轉場的Identifier標 識符設爲PickGame。

雙擊導航欄,將新場景命名爲Choose Game。設原型表項的Style爲Basic(基本),設重用標識符爲GameCell,如圖:

177.png

在 項目中使用Cocoa Touch Class模板新建一個Swift文件,命名爲GamePickerViewController,繼承UITableViewController。回 到Storyboard中將遊戲選擇頁面的Custom Class設爲`GamePickerViewController`。

如今爲新頁面添加數據。在GamePickerViewController.swift中,在開頭添加games屬性,而後重寫viewDidLoad函數,像這樣:

var games:[String]!
 
override func viewDidLoad() {
  super.viewDidLoad()
  games = ["Angry Birds",
           "Chess",
           "Russian Roulette",
           "Spin the Bottle",
           "Texas Hold'em Poker",
           "Tic-Tac-Toe"]
}

你剛剛新增了一個叫作`games`的字符串數組,並在`viewDidLoad()`中用寫定的內容填充數組。

而後以下替換數據源方法:

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
  return 1
}
 
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  return games.count
}
 
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("GameCell", forIndexPath: indexPath) as UITableViewCell
  cell.textLabel?.text = games[indexPath.row]
  return cell
}

上述代碼將`games`數組設爲數據源並替換表項的textLabel中的字符串值。

只要數據源準備就緒就應該能正常工做。運行App,點選Game行,新的遊戲選擇頁面會滑入屏幕。如今點擊各項不會有什麼效果,但因爲該頁面呈如今導航棧上,你能夠直接點擊返回按鈕,返回原來的添加玩家頁面。

188.png

不用寫代碼就能夠喚出新頁面,是否是很贊?只要按住control從靜態表項拖到新場景,寫的代碼只有填充表視圖的內容,並且通常來說比原地設計好的列表要靈活些(由於games數組更方便修改)。

固然新頁面要返回數據纔有用,爲此你要添加一個新的回退轉場。

在GamePickerViewController類的上面添加持有選中的遊戲的名稱和索引的屬性:

var selectedGame:String? = nil
var selectedGameIndex:Int? = nil

而後修改`cellForRowAtIndexPath:`:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let cell = tableView.dequeueReusableCellWithIdentifier("GameCell", forIndexPath: indexPath) as UITableViewCell
  cell.textLabel?.text = games[indexPath.row]
 
  if indexPath.row == selectedGameIndex {
    cell.accessoryType = .Checkmark
  } else {
    cell.accessoryType = .None
  }
  return cell
}

這會在當前所選遊戲對應的表項附上選中標記(對號),這對用戶體驗來講不可或缺。

接着添加`tableview(tableview:didSelectRowAtIndexPath:)`方法:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
  tableView.deselectRowAtIndexPath(indexPath, animated: true)
 
  //Other row is selected - need to deselect it
  if let index = selectedGameIndex {
    let cell = tableView.cellForRowAtIndexPath(NSIndexPath(forRow: index, inSection: 0))
    cell?.accessoryType = .None
  }
 
  selectedGameIndex = indexPath.row
  selectedGame = games[indexPath.row]
 
  //update the checkmark for the current row
  let cell = tableView.cellForRowAtIndexPath(indexPath)
  cell?.accessoryType = .Checkmark
}

這段代碼首先會取消選擇剛剛點選的行,外觀會從灰色高亮變回常規的白色,而後移除對號,並在剛剛點選的行上附加選中標記。

運行App,測試是否正常。點選一個遊戲名,相應行會附上選中標記,點選另外一個遊戲名,選中標記也隨之移動。

Game-picker-checkmark-408x500.png

按要求來講點選某行以後應該關閉該頁面,不過如今並無自動返回,由於還沒有綁定回退轉場。

在PlayerDetailsViewController.swift的類上面添加一個持有被選遊戲的屬性,以便以後在Player對象中保存。令其默認值爲"Chess",這樣一來新玩家總會有一個選定的遊戲。

var game:String = "Chess"

一樣在該文件中改寫`viewDidLoad()`以在靜態表項中游戲名稱:

override func viewDidLoad() {
  super.viewDidLoad()
  detailLabel.text = game
}

添加回退轉場方法:

@IBAction func selectedGame(segue:UIStoryboardSegue) {
  let gamePickerViewController = segue.sourceViewController as GamePickerViewController
  if let selectedGame = gamePickerViewController.selectedGame {
    detailLabel.text = selectedGame
    game = selectedGame
  }
  self.navigationController?.popViewControllerAnimated(true)
}

上述代碼會在用戶從選擇遊戲場景選中一個遊戲後執行。該方法按照選中的遊戲更新頁面上的label和game屬性,而後將GamePickerViewController彈出導航棧。

在Main.storyboard中按住control從表項拖到Exit出口對象,而後從彈出列表中選擇`selectedGame:`。

18_sb2_unwind_game.gif

設該回退轉場標識符爲SaveSelectedGame。

運行App試試看,建立新玩家,點選Game行並選擇一個遊戲。

19.jpg

不 幸的是,這個回退轉場方法是在`tableView(_:didSelectRowAtIndexPath:)`方法前執行的,因此 `selectedGameIndex`並未及時更新。幸運的是你能夠重寫`prepareForSegue(_:sender:)`方法,在轉場以前完 成更新操做。

在GamePickerViewController中添加`prepareForSegue(segue:)`方法:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  if segue.identifier == "SaveSelectedGame" {
    let cell = sender as UITableViewCell
    let indexPath = tableView.indexPathForCell(cell)
    selectedGameIndex = indexPath?.row
    if let index = selectedGameIndex {
      selectedGame = games[index]
    }
  }
}

`prepareForSegue(_:sender:)`的sender參數是引起轉場的對象,在這裏對應選中的遊戲表項,因此你能夠利用表項的indexPath來在games數組中肯定選中的遊戲並在轉場發生以前更新`selectedGame`。

如今運行App,選擇遊戲後玩家的遊戲信息會隨之更新了。

20_sb2_it_works-336x320.png

接下來改寫PlayerDetailsViewController的prepareForSegue方法來返回選中的遊戲,而不是寫定的"Chess"。這樣一來,完成添加玩家的操做後,Players場景中會顯示玩家實際選擇的遊戲。

在PlayerDetailsViewController.swift中以下改寫`prepareForSegue(_:sender:)`方法:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
  if segue.identifier == "SavePlayerDetail" {
    player = Player(name: nameTextField.text, game:game, rating: 1)
  }
}

完成添加玩家頁面並點擊Done後,玩家列表會更新正確的遊戲信息。

還有一點,當你選擇一個遊戲,返回添加玩家頁面,而後嘗試從新選擇遊戲的時候,以前選定的遊戲應該顯示選中標記。解決方法是在轉場時把PlayerDetailsViewController中保存的選中的遊戲傳給GamePickerViewController。

仍是在PlayerDetailsViewController.swift中,於`prepareForSegue(segue:,sender:)`方法的末尾添加如下代碼:

if segue.identifier == "PickGame" {
  let gamePickerViewController = segue.destinationViewController as GamePickerViewController
  gamePickerViewController.selectedGame = game
}

注意:如今有兩條檢查`segue.identifier`的 `if`語句。SavePlayerDetail是返回玩家列表的回退轉場,PickGame是前往遊戲選擇頁面的入棧轉場。添加的代碼會在 GamePickerViewController的視圖加載以前更新其中的`selectedGame`。

打開GamePickerViewController.swift並在`viewDidLoad()`末尾添加如下代碼:

if let game = selectedGame {
  selectedGameIndex = find(games, game)!
}

這兩行代碼獲取從 PlayerDetailsViewController傳進的selectedGame並將其轉換成正確的索引。`find()`函數會在games數 組中查找匹配selectedGame的String,而後返回匹配元素的索引,賦值給selectedGameIndex,這個索引用來在對應表項上設 置選中標記。

好。如今選擇遊戲頁面功能實現完成!

Marin-Danger-280x500.png

何去何從?

這是整個教程的示例項目,包含上述全部源代碼。

可喜可賀,如今你已經瞭解Storyboard編輯器的基本用法,可以建立包含多個視圖控制器並能經過轉場在場景之間切換的App!在一處集中管理多個視圖控制器和互相的關聯,讓總體把握App的樣子更加容易。

你也看到了自定義表視圖和表項有多麼容易。有了靜態表項,不用實現全部的數據源方法也能夠構建一些界面。

若是想深刻了解Storyboard,請參閱咱們的書籍iOS 8教程,其中涵蓋了最新的通用Storyboard(Universal Storyboard)。

相關文章
相關標籤/搜索