Storyboard使用教程二

在本系列Storyboard教程的第一部分巨税,我們已經(jīng)學(xué)習(xí)了如何使用Interface Builder創(chuàng)建并連接不同的視圖控制器,還有如何直接在Storyboard編輯器中創(chuàng)建自定義表項培他。

本教程的第二部分鹃两,也是最終部分,內(nèi)容包括segue(轉(zhuǎn)場)舀凛,static table view cell(靜態(tài)表項)俊扳,添加玩家頁面和游戲選擇頁面!

好猛遍,現(xiàn)在讓我們一起探索Storyboard的其他酷炫特性吧馋记!

轉(zhuǎn)場(Segue)

讓我們向Storyboard中繼續(xù)添加視圖控制器,創(chuàng)建一個讓用戶添加新玩家的頁面懊烤。

打開Main.storyboard抗果,在包含表視圖的那個Players場景的導(dǎo)航欄右側(cè)拖入一個Bar Button Item(欄按鈕項),在屬性檢查器中將Identifier設(shè)為Add奸晴,使其成為標(biāo)準(zhǔn)添加(加號)按鈕冤馏。


當(dāng)用戶點按這個按鈕時,你希望App會彈出一個模態(tài)頁面讓用戶輸入新玩家的詳細(xì)信息寄啼。

在Players場景的右邊拖入一個新的Navigation Controller(導(dǎo)航控制器)逮光。記得雙擊面板可以縮放畫面騰出空間。新加入的導(dǎo)航控制器附帶一個表視圖控制器墩划,很方便涕刚。

這里有個小技巧:選擇剛才在Players頁面里加入的加號按鈕,按住control鍵把它拖向新建的導(dǎo)航控制器乙帮,松手杜漠,在彈出的小選單中選擇modal(模態(tài))。

還記得嗎:當(dāng)Storyboard面板處于縮小狀態(tài)時察净,無法添加或修改內(nèi)容驾茴。如果在創(chuàng)建轉(zhuǎn)場時遇到問題,請嘗試雙擊放大氢卡!


現(xiàn)在Players頁面和導(dǎo)航控制器之間多了一個新箭頭锈至。

這種連接的類型叫做segue(轉(zhuǎn)場,讀作seg-way译秦,源自電影術(shù)語峡捡,原指兩個場景間的過渡銜接)击碗,表示一個頁面到另一個頁面的過渡。此前我們所見的Storyboard連接描述的都是視圖控制器的包含關(guān)系们拙,而轉(zhuǎn)場是用來切換頁面的稍途。轉(zhuǎn)場可以由點擊按鈕、表項砚婆、手勢等條件觸發(fā)晰房。

使用轉(zhuǎn)場的好處是,再也不用為呈現(xiàn)新頁面寫代碼了射沟,也不用把按鈕連接到IBAction方法上,你只需要在Storyboard中從一個欄按鈕項拖到下一個頁面就可以創(chuàng)建過渡了与境。(注:如果你的控件已經(jīng)綁定了IBAction連接验夯,該連接會被轉(zhuǎn)場屏蔽。)

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


這就是所謂的模態(tài)轉(zhuǎn)場共屈。新頁面完全覆蓋原頁面绑谣,在關(guān)閉模態(tài)頁面之前,用戶只能在新頁面進(jìn)行交互拗引。后面我們還會看到push(入棧)轉(zhuǎn)場借宵,這種轉(zhuǎn)場會把新頁面壓入導(dǎo)航控制器的導(dǎo)航棧(navigation stack)。

現(xiàn)在新頁面還沒什么用矾削,連關(guān)閉頁面返回都做不到壤玫,有去無回,因為轉(zhuǎn)場是單向操作哼凯。

為返回頁面欲间,Storyboard提供了unwind(回退)轉(zhuǎn)場。接下來我們要實現(xiàn)返回功能断部,主要分三個步驟:

  1. 創(chuàng)建讓用戶點選的控件猎贴,通常是個按鈕。

  2. 在你想返回的控制器創(chuàng)建回退方法蝴光。

  3. 在Storyboard中將控件與回退方法連接她渴。

首先打開Main.storyboard,選擇新的表視圖控制器場景(叫“Root View Controller”的那個)蔑祟。雙擊導(dǎo)航欄惹骂,把標(biāo)題改成“Add Player”。然后在導(dǎo)航欄添加兩個欄按鈕項做瞪,在屬性檢查器中設(shè)置左側(cè)按鈕的Identifier為Cancel对粪,右側(cè)按鈕為Done右冻,并將右側(cè)按鈕的Style改成Done。


接下來在項目中用Cocoa Touch Class模板添加一個新文件著拭,命名為PlayerDetailsViewController并令其繼承UITableViewController纱扭。要把這個類關(guān)聯(lián)到Storyboard,先切回Main.storyboard儡遮,選擇添加玩家的場景乳蛾,然后在身份檢查器(Identity inspector)中設(shè)Class為PlayerDetailsViewController。這個步驟我經(jīng)常忘掉鄙币,在此特地提醒肃叶,還請讀者牢記。

現(xiàn)在終于可以創(chuàng)建回退轉(zhuǎn)場了十嘿。在PlayersViewController.swift(不是detail那個)的類定義下面添加如下的回退方法:

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

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

這兩個方法在調(diào)用時都會解除這個控制器因惭。后面你會改寫savePlayerDetail,讓它名副其實地履行自己的職責(zé)绩衷。

最后回到Interface Builder蹦魔,把Cancel按鈕和Done按鈕連接到相應(yīng)的action方法上。按住control從欄按鈕拖到視圖控制器上面的出口(exit)對象上咳燕,然后從彈出的選單中選擇正確的action名稱勿决。


記住取消方法的方法名,創(chuàng)建回退轉(zhuǎn)場時招盲,App中的所有回退方法(形如@IBAction func methodname(segue:UIStoryboardSegue))都會在列表中顯示低缩,所以命名方法時要多加注意,避免混淆曹货。

運行App表制,點擊加號按鈕,然后測試Cancel和Done按鈕控乾。僅僅幾行代碼就可以實現(xiàn)如此功能么介。

靜態(tài)表項(Static Cell)

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


當(dāng)然這是一個分組表視圖(grouped table view)蜕衡,但不必為該表創(chuàng)建數(shù)據(jù)源壤短,也不必為此編寫cellForRowAtIndexPath方法,你可以直接在Interface Builder中完成設(shè)計慨仿。這個特性叫做靜態(tài)表項(static cell)久脯。

選中Add Player場景的表視圖,在屬性檢查器中設(shè)Content為Static Cells镰吆,把Style由Plain改成Grouped帘撰,并為表視圖設(shè)置兩個分段(section)。


修改Sections屬性值時万皿,編輯器會復(fù)制已有的分段摧找。(你也可以在左側(cè)的文檔大綱中選擇特定分段并復(fù)制核行。)

最終頁面每個分段應(yīng)該只有一行,請在面板或文檔大綱中選中并刪除多余的表項蹬耘。

在文檔大綱中選擇最上面的表視圖分段芝雪,在屬性選擇器中設(shè)Header字段值為Player Name。


向該分段內(nèi)拖入一個新的Text Field(文本字段)综苔,橫向拉長并移除邊框惩系,使文本字段控件融入周圍環(huán)境。設(shè)字體為 System 17.0 如筛,勾掉Adjust to Fit選項堡牡。


接下來我們要用Xcode的Assistant Editor(輔助編輯器)功能為該文本字段在PlayerDetailsViewController中創(chuàng)建一個outlet。在Storyboard中杨刨,點擊工具欄上的按鈕(圖標(biāo)是兩個套在一起的圓圈)打開輔助編輯器晤柄,應(yīng)該會自動打開PlayerDetailsViewController.swift(如果沒有,在右側(cè)的跳轉(zhuǎn)欄中選擇相應(yīng)文件)拭嫁。

選擇新建的文本字段,按住control拖到swift文件的類定義下面抓于。在彈出框中將新outlet命名為nameTextField并點擊Connect做粤。在點擊Connect后Xcode會在PlayersDetailViewController類中添加屬性并在Storyboard中建立連接:


為表項上的視圖創(chuàng)建outlet對于原型表項來說可能會遇到問題,這在上一部分的教程中提到過捉撮,不過靜態(tài)表項就不必?fù)?dān)心了怕品,因為每個靜態(tài)表項都只會有唯一的實例,把子視圖與視圖控制器的outlet連接完全沒問題巾遭。

把第二分段的靜態(tài)表項的Style設(shè)為Right Detail肉康,這會套用一個標(biāo)準(zhǔn)表項樣式,雙擊左側(cè)的label灼舍,把文本改為Game吼和,然后為該表項設(shè)定Disclosure Indicator(展開方向標(biāo))附件。


仿照剛才的Name文本字段骑素,為右面的label("Detail"的那個)創(chuàng)建outlet并命名為detailLabel炫乓,該表項上的label都是常規(guī)UILabel對象。在建立連接前選擇Detail文本字段時可能需要多次點擊献丑,請確保選擇的是label而不是整個表項末捣。完成后如圖:


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


目前在Storyboard中設(shè)計的頁面尺寸都符合iPhone 5的4英寸屏幕,高度為568點创橄。當(dāng)然你的App應(yīng)當(dāng)在不同的屏幕尺寸下正常工作箩做,你可以在Storyboard中預(yù)覽所有的尺寸。

在工具欄上點開輔助編輯器妥畏,選擇跳轉(zhuǎn)欄中的Preview邦邦。點擊輔助編輯器左下角的加號添加新的預(yù)覽尺寸安吁,如果想刪除一個屏幕尺寸,選中并按delete鍵即可圃酵。


一個簡單的評分App不需要什么花哨的東西柳畔,只是使用表視圖控制器,頁面自動縮放以填滿屏幕空間郭赐。當(dāng)你想為不同的屏幕尺寸適配布局時薪韩,你需要使用Auto Layout和Size Classes。

構(gòu)建并運行App捌锭,你會注意到添加玩家頁面依然是空白俘陷!


表視圖控制器在使用靜態(tài)表項時不需要數(shù)據(jù)源,而之前你用Xcode模板創(chuàng)建的PlayerDetailsViewController類中依然有部分?jǐn)?shù)據(jù)源相關(guān)代碼观谦,靜態(tài)表項因此無法正常工作拉盾,所以靜態(tài)內(nèi)容沒有顯示出來。我們這就來解決問題豁状!

打開PlayerDetailsViewController.swift文件捉偏,刪除這一條代碼往下的所有內(nèi)容(注意不要刪掉類自己的括號):

// MARK: - Table view data source

現(xiàn)在,自從加入這個類以后Xcode顯示的那幾條警告(warning)也應(yīng)該消失了泻红。

運行App夭禽,檢查使用靜態(tài)表項的新頁面。完全沒有寫代碼谊路,其實剛才還刪了一段代碼讹躯!

還要了解一點:靜態(tài)表項只在UITableViewController中有效,雖然Interface Builder允許你在常規(guī)UIViewController中的表視圖對象里添加靜態(tài)表項缠劝,運行時不會發(fā)揮作用潮梯,原因是UITableViewController中額外實現(xiàn)了一些用來處理靜態(tài)表項數(shù)據(jù)源的操作。在項目中誤用的話Xcode甚至?xí)芙^編譯惨恭,輸出報錯信息:“Illegal Configuration: Static table views are only valid when embedded in UITableViewController instances”秉馏。

另一方面,原型表項在常規(guī)視圖內(nèi)的表視圖中可以正常工作脱羡,但在nib中就沒戲了沃饶。目前來講,使用原型表項或靜態(tài)表項就必須使用Storyboard轻黑。

你也有可能想在一個表視圖中混合使用靜態(tài)表項和常規(guī)的動態(tài)表項糊肤,很遺憾的是目前的SDK對此支持欠佳。如果你的App有這種需求氓鄙,請參考蘋果開發(fā)者官方論壇上的相關(guān)帖子尋求可行方案馆揉。

注:如果構(gòu)建的頁面上包含的靜態(tài)表項多到無法在可視范圍內(nèi)全部展示,你可以在Interface Builder中直接利用滾動手勢查看抖拦,這個功能可能不容易發(fā)現(xiàn)升酣,但確實管用舷暮。

不過總的來說該寫代碼的地方只能靠代碼,甚至靜態(tài)表項的表視圖也是如此噩茄。前面在把文本字段拖進(jìn)第一個表項的時候下面,你可能發(fā)現(xiàn)尺寸不大合適,文本字段周圍有一點白邊绩聘,而且用戶看不到文本字段的實際范圍沥割,如果正好點在邊框上,沒有彈出鍵盤凿菩,用戶會感到困惑机杜。

為避免這種情況,你應(yīng)該讓那一行任意位置接受的點擊都可以喚出鍵盤衅谷。要這樣做很容易椒拗,打開PlayerDetailsViewController.swift并如下添加

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

代碼的意思是如果用戶點按第一個表項,App應(yīng)該激活相應(yīng)文本字段获黔。該分段只有一個表項蚀苛,你只需使用分段的索引。設(shè)文本字段為第一響應(yīng)者會自動喚出鍵盤玷氏。這只是一小處用戶體驗優(yōu)化堵未,但就是這樣一個小細(xì)節(jié)可以給用戶省去一點煩惱。

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

另外耻陕,還應(yīng)該在Storyboard的屬性檢查器中把相應(yīng)表項的Selection Style設(shè)為None(原本是Default),否則用戶點按文本字段周圍的邊框時該行會高亮刨沦。


好啦诗宣,添加玩家頁面設(shè)計完成。現(xiàn)在我們要實現(xiàn)功能想诅。

為添加玩家頁面實現(xiàn)功能

現(xiàn)在先不管Game這行召庞,只輸入玩家名稱。

當(dāng)用戶點擊Cancel按鈕時来破,頁面關(guān)閉篮灼,用戶剛剛輸入的數(shù)據(jù)隨之作廢。這部分功能直接用回退轉(zhuǎn)場已經(jīng)實現(xiàn)好了徘禁。

而當(dāng)用戶點擊Done時诅诱,你應(yīng)該創(chuàng)建一個新的Player對象,參照用戶輸入填充屬性后更新玩家列表送朱。

轉(zhuǎn)場即將發(fā)生時娘荡,prepareForSegue(:sender:)會被調(diào)用干旁。你可以重寫這個方法,在退出視圖之前將數(shù)據(jù)保存到一個新的Player對象中炮沐。

注:不要擅自調(diào)用prepareForSegue方法争群,這是UIKit通知你一個轉(zhuǎn)場剛剛被觸發(fā)的消息。

在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:)方法判斷轉(zhuǎn)場的標(biāo)識符是否為SavePlayerDetail,當(dāng)且僅當(dāng)判定結(jié)果為真時遏餐,創(chuàng)建一個新的Player實例伦腐,其中g(shù)ame和rating均取默認(rèn)值。如果此時運行失都,App會崩潰柏蘑,因為不存在標(biāo)識符SavePlayerDetail`,player不會被實例化粹庞,結(jié)合前面的隱式解包可選量定義咳焚,引發(fā)運行時錯誤。

小提示:如果App出現(xiàn)詭異的崩潰問題庞溜,而且代碼看起來似乎并無邏輯錯誤革半,那么可能是在代碼中刪除過對象或修改過對象名,以致Storyboard引用對象出錯流码。

在Main.storyboard中又官,在文檔大綱里找到Add Player場景,選擇連接到savePlayerDetail這個action的回退轉(zhuǎn)場漫试,將其標(biāo)識符改為SavePlayerDetail


然后選擇連接到cancelToPlayersViewController的回退轉(zhuǎn)場六敬,將其標(biāo)識符改為CancelPlayerDetail。以供prepareForSegue(_:sender:)方法判斷標(biāo)識符驾荣。

轉(zhuǎn)到PlayersViewController類外构,如下修改回退轉(zhuǎn)場方法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)
}

這會通過傳入方法的轉(zhuǎn)場引用獲取一個指向PlayerDetailsViewController的引用,并借此向數(shù)據(jù)源中使用的Player數(shù)組添加新的Player對象播掷,然后通知表視圖在末尾新增了一行审编,因為表視圖和數(shù)據(jù)源應(yīng)當(dāng)保持同步。

你可能會直接調(diào)用tableView.reloadData()歧匈,但還是為新行插入的操作加入動畫效果比較好垒酬。UITableViewRowAnimation.Automatic會以插入新行的位置自動選用合適的動畫,十分方便。

試試看伤溉,現(xiàn)在應(yīng)該可以向列表中加入新玩家了般码!

性能

現(xiàn)在Storyboard已經(jīng)有好幾個視圖控制器了,你或許會擔(dān)心性能問題乱顾,不過一次載入整個Storyboard并不是什么苦活板祝,Storyboard不會立即實例化所有的視圖控制器,立即載入的只有初始視圖控制器走净。而由于這里的初始視圖控制器是一個分頁欄控制器券时,包含的兩個視圖控制器也會被載入(第一個分頁標(biāo)簽的Players場景和第二個分頁標(biāo)簽的場景)。

其他視圖控制器只有在轉(zhuǎn)場過去的時候才會被實例化伏伯。而當(dāng)關(guān)閉視圖控制器的時候橘洞,它們會立即被釋放亥宿,所以內(nèi)存中只有活躍使用的視圖控制器襟雷,就好像分別使用nib一樣埠褪。

實踐是檢驗真理的唯一標(biāo)準(zhǔn)停局,在PlayerDetailsViewController類中添加構(gòu)造器(initializer)和析構(gòu)器(deinitializer):

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

deinit {
     println("deinit PlayerDetailsViewController")
}

你剛剛重寫了init(coder:)deinit方法,讓它們向Xcode調(diào)試面板輸出信息〕薰荆現(xiàn)在運行App怜俐,打開添加玩家頁面制圈,你會發(fā)現(xiàn)視圖控制器只有在被打開的時候才會分配候引。

關(guān)閉添加玩家頁面的時候侯养,無論是點擊Cancel還是Done都會看到deinit析構(gòu)器的println()輸出。如果再次打開這個頁面澄干,你還會看到init(coder:)的輸出逛揩,這樣你應(yīng)該相信這個事實了:視圖控制器是按需加載的,就像手動載入nib一樣麸俘。

注:如果你以前用過nib辩稽,那么你應(yīng)該會很熟悉構(gòu)造器init(coder:),這部分機制延續(xù)到了Storyboard中:使用的方法依然是init(coder:)疾掰,awakeFromNib()viewDidLoad()搂誉。Storyboard可以看成附帶了過渡信息和關(guān)聯(lián)信息的一系列nib的集合徐紧,而Storyboard內(nèi)的視圖和視圖控制器使用與nib相同的方式編碼并解析静檬。

游戲選擇頁面

在添加玩家頁面中點選Game行應(yīng)該打開一個新頁面并讓用戶從列表中選擇一個游戲,這意味著下一步要加入另外一個表視圖控制器并级,不過這次的頁面不是模態(tài)顯示拂檩,而是壓入導(dǎo)航棧。

向Storyboard中拖入一個新的表視圖控制器嘲碧,在添加玩家頁面中選擇Game表項(確保選中的是整個表項稻励,而不是其中的label),然后按住control拖到新建的表視圖控制器,在兩者之間創(chuàng)建轉(zhuǎn)場望抽。在彈出的選單中選擇轉(zhuǎn)場類型為Push加矛,然后在屬性檢查器中把轉(zhuǎn)場的Identifier標(biāo)識符設(shè)為PickGame。

雙擊導(dǎo)航欄煤篙,將新場景命名為Choose Game斟览。設(shè)原型表項的Style為Basic(基本),設(shè)重用標(biāo)識符為GameCell辑奈,如圖:

1421214153697020.png

在項目中使用Cocoa Touch Class模板新建一個Swift文件苛茂,命名為GamePickerViewController,繼承UITableViewController鸠窗〖搜颍回到Storyboard中將游戲選擇頁面的Custom Class設(shè)為GamePickerViewController

現(xiàn)在為新頁面添加數(shù)據(jù)稍计。在GamePickerViewController.swift中躁绸,在開頭添加games屬性,然后重寫viewDidLoad函數(shù)臣嚣,像這樣:

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的字符串?dāng)?shù)組涨颜,并在viewDidLoad()中用寫定的內(nèi)容填充數(shù)組。

然后如下替換數(shù)據(jù)源方法:

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數(shù)組設(shè)為數(shù)據(jù)源并替換表項的textLabel中的字符串值茧球。

只要數(shù)據(jù)源準(zhǔn)備就緒就應(yīng)該能正常工作庭瑰。運行App,點選Game行抢埋,新的游戲選擇頁面會滑入屏幕〉穑現(xiàn)在點擊各項不會有什么效果,但由于該頁面呈現(xiàn)在導(dǎo)航棧上揪垄,你可以直接點擊返回按鈕穷吮,返回原來的添加玩家頁面。


不用寫代碼就可以喚出新頁面饥努,是不是很贊捡鱼?只要按住control從靜態(tài)表項拖到新場景,寫的代碼只有填充表視圖的內(nèi)容酷愧,而且一般來講比原地設(shè)計好的列表要靈活些(因為games數(shù)組更方便修改)驾诈。

當(dāng)然新頁面要返回數(shù)據(jù)才有用,為此你要添加一個新的回退轉(zhuǎn)場溶浴。

在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
}

這會在當(dāng)前所選游戲?qū)?yīng)的表項附上選中標(biāo)記(對號)乍迄,這對用戶體驗來說不可或缺。

接著添加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
}

這段代碼首先會取消選擇剛剛點選的行士败,外觀會從灰色高亮變回常規(guī)的白色闯两,然后移除對號,并在剛剛點選的行上附加選中標(biāo)記。

運行App漾狼,測試是否正常重慢。點選一個游戲名,相應(yīng)行會附上選中標(biāo)記逊躁,點選另一個游戲名伤锚,選中標(biāo)記也隨之移動。


按要求來說點選某行之后應(yīng)該關(guān)閉該頁面志衣,不過現(xiàn)在并沒有自動返回屯援,因為尚未綁定回退轉(zhuǎn)場。

在PlayerDetailsViewController.swift的類上面添加一個持有被選游戲的屬性念脯,以便之后在Player對象中保存狞洋。令其默認(rèn)值為"Chess",這樣一來新玩家總會有一個選定的游戲绿店。

var game:String = "Chess"
同樣在該文件中改寫viewDidLoad()以在靜態(tài)表項中游戲名稱:

override func viewDidLoad() {
super.viewDidLoad()
detailLabel.text = game
}
添加回退轉(zhuǎn)場方法:

@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)
}

上述代碼會在用戶從選擇游戲場景選中一個游戲后執(zhí)行吉懊。該方法按照選中的游戲更新頁面上的label和game屬性,然后將GamePickerViewController彈出導(dǎo)航棧假勿。

在Main.storyboard中按住control從表項拖到Exit出口對象借嗽,然后從彈出列表中選擇selectedGame:


設(shè)該回退轉(zhuǎn)場標(biāo)識符為SaveSelectedGame转培。

運行App試試看恶导,創(chuàng)建新玩家,點選Game行并選擇一個游戲浸须。


不幸的是惨寿,這個回退轉(zhuǎn)場方法是在tableView(_:didSelectRowAtIndexPath:)方法前執(zhí)行的,所以selectedGameIndex并未及時更新删窒。幸運的是你可以重寫prepareForSegue(_:sender:)方法裂垦,在轉(zhuǎn)場之前完成更新操作。

在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參數(shù)是引發(fā)轉(zhuǎn)場的對象肌索,在這里對應(yīng)選中的游戲表項蕉拢,所以你可以利用表項的indexPath來在games數(shù)組中確定選中的游戲并在轉(zhuǎn)場發(fā)生之前更新selectedGame

現(xiàn)在運行App诚亚,選擇游戲后玩家的游戲信息會隨之更新了晕换。


接下來改寫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后,玩家列表會更新正確的游戲信息。

還有一點或辖,當(dāng)你選擇一個游戲瘾英,返回添加玩家頁面,然后嘗試重新選擇游戲的時候颂暇,之前選定的游戲應(yīng)該顯示選中標(biāo)記缺谴。解決方法是在轉(zhuǎn)場時把PlayerDetailsViewController中保存的選中的游戲傳給GamePickerViewController。

還是在PlayerDetailsViewController.swift中耳鸯,于prepareForSegue(segue:,sender:)方法的末尾添加以下代碼:

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

注意:現(xiàn)在有兩條檢查segue.identifierif語句湿蛔。SavePlayerDetail是返回玩家列表的回退轉(zhuǎn)場,PickGame是前往游戲選擇頁面的入棧轉(zhuǎn)場县爬。添加的代碼會在GamePickerViewController的視圖加載之前更新其中的selectedGame阳啥。

打開GamePickerViewController.swift并在viewDidLoad()末尾添加以下代碼:

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

這兩行代碼獲取從PlayerDetailsViewController傳進(jìn)的selectedGame并將其轉(zhuǎn)換成正確的索引。find()函數(shù)會在games數(shù)組中查找匹配selectedGame的String财喳,然后返回匹配元素的索引察迟,賦值給selectedGameIndex,這個索引用來在對應(yīng)表項上設(shè)置選中標(biāo)記耳高。

好≡浚現(xiàn)在選擇游戲頁面功能實現(xiàn)完成!

何去何從泌枪?

可喜可賀概荷,現(xiàn)在你已經(jīng)了解Storyboard編輯器的基本用法,能夠創(chuàng)建包含多個視圖控制器并能通過轉(zhuǎn)場在場景之間切換的App碌燕!在一處集中管理多個視圖控制器和互相的關(guān)聯(lián)乍赫,讓整體把握App的樣子更加容易。

你也看到了自定義表視圖和表項有多么容易陆蟆。有了靜態(tài)表項雷厂,不用實現(xiàn)所有的數(shù)據(jù)源方法也可以構(gòu)建一些界面。

原文地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末叠殷,一起剝皮案震驚了整個濱河市改鲫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌林束,老刑警劉巖像棘,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異壶冒,居然都是意外死亡缕题,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進(jìn)店門胖腾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來烟零,“玉大人瘪松,你說我怎么就攤上這事∠前ⅲ” “怎么了宵睦?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長墅诡。 經(jīng)常有香客問我壳嚎,道長,這世上最難降的妖魔是什么末早? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任烟馅,我火速辦了婚禮,結(jié)果婚禮上然磷,老公的妹妹穿的比我還像新娘焙糟。我一直安慰自己,他們只是感情好样屠,可當(dāng)我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布穿撮。 她就那樣靜靜地躺著,像睡著了一般痪欲。 火紅的嫁衣襯著肌膚如雪悦穿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天业踢,我揣著相機與錄音栗柒,去河邊找鬼。 笑死知举,一個胖子當(dāng)著我的面吹牛瞬沦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播雇锡,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼逛钻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了锰提?” 一聲冷哼從身側(cè)響起曙痘,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎立肘,沒想到半個月后边坤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡谅年,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年茧痒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片融蹂。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡旺订,死狀恐怖弄企,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情耸峭,我是刑警寧澤桩蓉,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布淋纲,位于F島的核電站劳闹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏洽瞬。R本人自食惡果不足惜本涕,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望伙窃。 院中可真熱鬧菩颖,春花似錦、人聲如沸为障。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鳍怨。三九已至呻右,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鞋喇,已是汗流浹背声滥。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留侦香,地道東北人落塑。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像罐韩,于是被迫代替她去往敵國和親憾赁。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,507評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Storyboard是最先在iOS 5引入的一項振奮人心的特性散吵,大幅縮減構(gòu)建App用戶界面所需的時間缠沈。 要介紹St...
    余一波_Bobby閱讀 61,583評論 11 76
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件错蝴、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,119評論 4 61
  • 前言的前言 唐巧前輩在微信公眾號「iOSDevTips」以及其博客上推送了我的文章后洲愤,我的 Github 各項指標(biāo)...
    VincentHK閱讀 5,375評論 3 44
  • 很多媽媽說沒時間柬赐,要帶孩子做家務(wù)。有的媽媽說官紫,白天上班晚上帶孩子肛宋,沒時間州藕。貌似大家確實很忙。 有個現(xiàn)象不知道大家發(fā)...
    雙胞胎媽媽_9a17閱讀 172評論 0 0
  • 當(dāng)我不開心酝陈、郁悶床玻、失意或失敗時 我只是想朝我最親近的你發(fā)發(fā)牢騷 希望聽幾句安慰的話語 而不是你喋喋不休的教育 或者...
    簡晨332閱讀 130評論 0 0