制作table view cell的幾種方法
在AllListsViewController中創(chuàng)建table view cell的方法比在ChecklistViewController中略復(fù)雜一些颠毙。在后者中你僅僅是通過簡單的一個(gè)語句就獲得了一個(gè)新的table view cell:
let cell = tableView.dequeueReusableCell(withIdentifier: "ChecklistItem",for: indexPath)
但是在AllListsViewController中為了實(shí)現(xiàn)同樣的目的我們寫了一大堆代碼:
let cellIdentifier = "Cell"
if let cell =
tableView.dequeueReusableCell(withIdentifier: cellIdentifier) {
return cell
} else {
return UITableViewCell(style: .default,reuseIdentifier: cellIdentifier)
}
這里我們還是調(diào)用了dequeueReusableCell(withIdentifier)斯入,只是以前我們?cè)诠适履0嬷蟹胖昧薱ell并且給了它一個(gè)身份標(biāo)示,而這次沒有蛀蜜。
如果這個(gè)table view找不到任何可重用的cell刻两,這個(gè)方法會(huì)返回nil,這時(shí)你不得不手動(dòng)創(chuàng)建cell滴某,這就是else后面跟的代碼的作用磅摹。
這實(shí)際上是兩種不同類型的dequeueReusableCell(...)滋迈,其中一個(gè)有IndexPath參數(shù)而另一個(gè)沒有。在AllListsViewController中我們使用的是沒有IndexPath參數(shù)的這一個(gè)户誓。兩者的區(qū)別在于有IndexPath參數(shù)的這一個(gè)僅用于標(biāo)準(zhǔn)cell饼灿。如果在AllListsViewController中使用有IndexPath參數(shù)的這個(gè)方法,app就會(huì)崩潰掉帝美。
制作cell有四種方法:
1碍彭、使用標(biāo)準(zhǔn)cell。這是最簡單也是最快的一種方法证舟。我們?cè)贑hecklistViewController中做的就是硕旗。
2、使用靜態(tài)cell女责。你在Add/Edit界面中使用的就是靜態(tài)cell漆枚。靜態(tài)cell最大的優(yōu)勢(shì)就是不用給它提供數(shù)據(jù)源方法,適用于你提前知道cell內(nèi)容的情況抵知。
3墙基、使用nib文件。一個(gè)nib(也叫做XIB)就像一個(gè)迷你的僅僅包含一個(gè)自定義的UITableViewCell對(duì)象的故事模版刷喜。這和使用標(biāo)準(zhǔn)cell非常相似残制,只是你是在故事模版之外使用它。
4掖疮、手動(dòng)創(chuàng)建初茶,就是我們?cè)贏llListsViewController中使用的方法。在早期的iOS版本中浊闪,只有這一種方法恼布。這種方法要復(fù)雜一些,但是更加靈活搁宾。
當(dāng)你手動(dòng)創(chuàng)建一個(gè)cell時(shí)折汞,你需要指定一個(gè)確定的cell style,就會(huì)得到一個(gè)已經(jīng)包含標(biāo)簽和圖片的預(yù)置布局的cell盖腿。
在All Lists View Controller中爽待,你使用了“Default”style,在稍后你會(huì)將它切換為“Subtitle”翩腐,這會(huì)在主標(biāo)簽的下方鸟款,給你一個(gè)小一點(diǎn)的次級(jí)標(biāo)簽。
使用標(biāo)準(zhǔn)cell style意味著你不需要設(shè)計(jì)你自己的cell布局茂卦。對(duì)于大多數(shù)app而言標(biāo)準(zhǔn)cell已經(jīng)足夠用了何什。
標(biāo)準(zhǔn)cell和靜態(tài)cell都可以使用標(biāo)準(zhǔn)cell style。標(biāo)準(zhǔn)cell和靜態(tài)cell的默認(rèn)style都是“Custom”疙筹,這種style要求你使用自己的標(biāo)簽富俄,但是你可以通過界面建造器將它改變?yōu)閮?nèi)建的style禁炒。
最后,你需要注意的是:有時(shí)我看到其他人是這樣寫代碼的霍比,使用代碼為每一行創(chuàng)建一個(gè)新的cell而不是試著重用cell幕袱。你千萬不要這樣做!一定要首先向table view請(qǐng)求看看是否有可以重用的cell悠瞬,使用dequeueReusableCell(...)這個(gè)方法们豌。
為每一行都創(chuàng)建一個(gè)新的cell,會(huì)使app變慢浅妆,創(chuàng)建一個(gè)對(duì)象總是比重用一個(gè)對(duì)象要慢望迎。所以為每一行都創(chuàng)建一個(gè)新的cell會(huì)占據(jù)大量內(nèi)存,為了用戶著想凌外,你也應(yīng)該重用cell辩尊。
查看待辦事項(xiàng)分類
目前,由AllListsViewController中的lists數(shù)組組成的數(shù)據(jù)模型包含了少量的Checklist對(duì)象康辑。數(shù)據(jù)模型中同時(shí)還有來自ChecklistViewController的items數(shù)組摄欲,其中包含ChecklistItem對(duì)象。
你也許已經(jīng)注意到了疮薇,當(dāng)你點(diǎn)擊任何一行時(shí)胸墙,無論是哪一行,都會(huì)展示一模一樣的待辦事項(xiàng)按咒。
而實(shí)際上迟隅,每個(gè)待辦事項(xiàng)分類,都應(yīng)該對(duì)應(yīng)不同的待辦事項(xiàng)內(nèi)容励七。我們之后會(huì)完成這一工作智袭。
首先,我們來設(shè)置好映射被選擇的待辦事項(xiàng)分類的名稱呀伙,作為界面的標(biāo)題补履。
打開ChecklistViewController.swift添坊,添加一個(gè)實(shí)例變量:
var checklist: Checklist!
過會(huì)我再講為什么這必須是個(gè)可選型剿另。
還是在ChecklistViewController.swift中,將viewDidLoad()方法修改為:
override func viewDidLoad() {
super.viewDidLoad()
title = checklist.name
}
這一步的作用是改變界面的標(biāo)題贬蛙,就是導(dǎo)航欄的標(biāo)題雨女,將導(dǎo)航欄的標(biāo)題修改為Checklist對(duì)象的名稱。
當(dāng)執(zhí)行轉(zhuǎn)場時(shí)阳准,你會(huì)將這個(gè)checklist對(duì)象給到ChecklistViewController氛堕。
打開AllListViewController.swift,將tableView(didSelectRowAt)修改為:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let checklist = lists[indexPath.row]
performSegue(withIdentifier: "ShowChecklist", sender: checklist)
}
和以前一樣,你使用performSegue()來執(zhí)行轉(zhuǎn)場野蝇。這個(gè)方法之前有一個(gè)參數(shù)sender讼稚,之前是nil±ㄈ澹現(xiàn)在你用來傳遞用戶點(diǎn)擊的那一行的Checklist對(duì)象。
你可以在sender參數(shù)中放置任何東西锐想。如果你通過故事模版執(zhí)行轉(zhuǎn)場(而不是像現(xiàn)在這樣手動(dòng)轉(zhuǎn)場)帮寻,那么sender就會(huì)引用被觸發(fā)的空控件,例如用于Add按鈕的UIBarButton對(duì)象或者用于列表中某一行的UITableViewCell赠摇。
但是因?yàn)槟闶峭ㄟ^手動(dòng)開始轉(zhuǎn)場的固逗,所以你可以在sender中放入最方便的對(duì)象。
將Checklist對(duì)象放入sender參數(shù)時(shí)藕帜,還不會(huì)將這個(gè)對(duì)象給到ChecklistViewController烫罩。這一步發(fā)生在“prepare-for-segue”中,你還沒有在代碼里寫這個(gè)方法洽故。
在AllListsViewController.swift中添加以下方法:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowChecklist" {
let controller = segue.destination as! ChecklistViewController
controller.checklist = sender as! Checklist
}
}
你之前應(yīng)該見到過這個(gè)方法贝攒。prepare(for:sender:)在轉(zhuǎn)場執(zhí)行后立即被調(diào)用。你可以在這里时甚,在新的視圖還沒有在屏幕上可視化之前設(shè)置新視圖的屬性饿这。
??:轉(zhuǎn)場的目標(biāo)是ChecklistVieController,不是UINavigationController撞秋,這和之前有點(diǎn)不同长捧。
到Add/edit界面的轉(zhuǎn)場是一種modally presented(這個(gè)真心不知道怎么翻譯)方式,針對(duì)與嵌入導(dǎo)航控制器中的視圖控制器吻贿。
而這次是“Push”型的轉(zhuǎn)場串结,直接轉(zhuǎn)到Checklist View Controller。
看看故事模版就知道在All Lists界面和Checklist界面之間沒有導(dǎo)航控制器舅列。這個(gè)轉(zhuǎn)場直接從一個(gè)視圖轉(zhuǎn)到另一個(gè)肌割。
在prepare(for:sender:)中,你需要將被點(diǎn)擊行的Checklist對(duì)象給到ChecklistViewController帐要。這就是為什么之前你將Checklist對(duì)象放入sender參數(shù)中的原因把敞。(你也可以將Checklist對(duì)象臨時(shí)存儲(chǔ)到一個(gè)實(shí)例變量里,但是把這個(gè)對(duì)象放入sender參數(shù)中更加簡單)
所有這一切發(fā)生在ChecklistViewController被加載前榨惠,ChecklistViewController被實(shí)例化時(shí)的一瞬間奋早。這就是說它的viewDidLoad()方法在prepare(for:sender:)之后被調(diào)用。
在這一時(shí)刻赠橙,這個(gè)視圖控制器的checklist屬性被來自sender的Checklist對(duì)象填充耽装,并且viewDidLoad()可以據(jù)此修改界面的標(biāo)題。
這一系列過程解釋了為什么checklist屬性被聲明為可選型期揪。因?yàn)橹钡秸{(diào)用viewload()前掉奄,它都是nil。
nil通常不是Swift中允許的變量取值凤薛,但是可選型例外姓建。
之前我們聲明可選型時(shí)用的是問號(hào)诞仓,這里是一個(gè)感嘆號(hào),感嘆號(hào)的作用和問號(hào)非常類似速兔,區(qū)別在于用感嘆號(hào)時(shí)狂芋,你不需要用if let去對(duì)它進(jìn)行解包。
使用這種隱式解包可選型時(shí)憨栽,需要非常小心帜矾,因?yàn)樗鼈儧]有任何保護(hù)措施。
運(yùn)行app屑柔,點(diǎn)擊一個(gè)待辦事型分類屡萤,轉(zhuǎn)入的屏幕界面的標(biāo)題會(huì)顯示為這個(gè)待辦事項(xiàng)分類的名稱。
注意一點(diǎn)掸宛,把Checklist對(duì)象給到ChecklistViewController并不會(huì)形成一個(gè)拷貝死陆。
你僅僅是傳遞這個(gè)對(duì)象的一個(gè)引用到視圖控制器,用戶對(duì)Checklist對(duì)象做出的任何變更唧瘾,都會(huì)體現(xiàn)在AllListsViewController上措译。
這兩個(gè)視圖控制器讀取的都是同一個(gè)Checklist對(duì)象。過會(huì)在Checklist中添加新的ChecklistItem時(shí)饰序,這一點(diǎn)會(huì)成為你的便利條件领虹。
類型扮演(type cast)
在prepare(for:sender:)中,你寫了這樣的代碼:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
...
controller.checklist = sender as! Checklist
...
}
這里的as!是什么呢求豫?
如果你足夠細(xì)心的話塌衰,你會(huì)注意到“as something”已經(jīng)出現(xiàn)過好幾次了。這就是類型扮演(type cast)
類型扮演通知Swift解釋具有不同數(shù)據(jù)類型的值蝠嘉。這和電影中的某一個(gè)演員正好相反最疆,在電影中一個(gè)演員只扮演一個(gè)角色,而在swift中蚤告,類型扮演的實(shí)際作用就是改變了對(duì)象的角色努酸。
上面的方法中,sender的參數(shù)是Any?杜恰,這意味著這個(gè)參數(shù)可以是任何類型的對(duì)象:一個(gè)UIBarButtonItem获诈,一個(gè)UITableViewCell,或者一個(gè)Checklist對(duì)象箫章。感謝這里的問號(hào)烙荷,使得它甚至可以為nil镜会。
但是controller.checklist總是期待一個(gè)合適的Checklist對(duì)象檬寂,它無法處理其他對(duì)象,比如UITableViewController戳表,因此桶至,swift需要你只能把Checklist對(duì)象放入checklist屬性中昼伴。
通過“sender as! Checklist”,你告訴了Swift它可以安全的將sender作為Checklist對(duì)象處理镣屹。
另一個(gè)類型扮演的例子是:
let controller = segue.destination as! ChecklistViewController
轉(zhuǎn)場的destination(目的地)屬性引用轉(zhuǎn)場結(jié)束時(shí)接受到的視圖控制器圃郊,顯然 ,蘋果的工程師無法提前預(yù)言這個(gè)視圖控制器就是我們命名的ChecklistViewController女蜈。
所以你不得不在讀取任何這個(gè)對(duì)象的屬性前持舆,先將它由通用類型UIViewController扮演為這個(gè)app中存在的ChecklistViewController。
在舉一個(gè)例子伪窖,在loadChecklistItems()中:
items = unarchiver.decodeObjectForKey("ChecklistItems")
as! [ChecklistIt]
NSKeyedUnarchiver將"ChecklistItems"鍵值下凍結(jié)的對(duì)象解碼到一個(gè)數(shù)組中逸寓,但是你必須告訴swift這確實(shí)是一個(gè)包含ChecklistItem對(duì)象的數(shù)組。
沒有類型扮演的的話覆山,swfit會(huì)認(rèn)為這是任何類型竹伸,這樣就會(huì)造成和items數(shù)組的數(shù)據(jù)類型不相容的事情發(fā)生。
還有一種使用as?的類型扮演簇宽,這是用于可選型的類型扮演勋篓,或者說這個(gè)類型扮演可能會(huì)為nil。我們會(huì)在后面接觸到這種例子魏割。
如果你不太理解這些內(nèi)容也不要擔(dān)心譬嚣,我們會(huì)通過大量的例子讓你消化這個(gè)內(nèi)容。
你使用類型扮演的最終原因是钞它,iOS架構(gòu)的通信原理是由Object-C寫成的孤荣,swift在類型上的要求比OC要寬松一些,在OC中你需要更加精確的指明類型须揣。
添加和編輯待辦事項(xiàng)分類
讓我們快速完成添加和編輯待辦事項(xiàng)分類功能盐股。這是另一個(gè)擁有靜態(tài)cell的UITableViewController。
如果之前的代碼你已經(jīng)了然于心了耻卡,那么現(xiàn)在工作對(duì)你就是小菜一碟疯汁!
在工程導(dǎo)航器中新增一個(gè)Cocoa Touch Class模版或者直接新增一個(gè)swift文件,取名為ListDetailViewController幌蚊。
將模版中原有的內(nèi)容都刪掉,替換為下面的語句:
import UIKit
protocol ListDetailViewControllerDelegate: class {
func listDetailViewControllerDidCancel(_ controller: ListDetailViewController)
func listDetailViewController(_ controller: ListDetailViewController,didFinishAdding checklist: Checklist)
func listDetailViewController(_ controller: ListDetailViewController,didFinishEditing checklist: Checklist)
}
class ListDetailViewController: UITableViewController,UITextFieldDelegate {
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var doneBarButton: UIBarButtonItem!
weak var delegate: ListDetailViewControllerDelegate?
var checklistToEdit: Checklist?
}
我僅僅是把ItemDetailViewController.swift中的內(nèi)容拷貝過來改了改名字溢豆。同時(shí)注意一下瘸羡,你現(xiàn)在要處理的是Checklist對(duì)象,而不是ChecklistItem队他。
添加一個(gè)viewDidLoad()方法:
override func viewDidLoad() {
super.viewDidLoad()
if let checklist = checklistToEdit {
title = "Edit Checklist"
textField.text = checklist.name
doneBarButton.isEnabled = true
}
}
這樣當(dāng)用戶編輯已經(jīng)存在的待辦事項(xiàng)分類時(shí),可以將界面的標(biāo)題修改為Edit Checklist麸折,并且將被修改的待辦事項(xiàng)分類的名稱放入text field。
同時(shí)也添加一個(gè)viewWillAppear()方法窜锯,用于自動(dòng)彈出小鍵盤:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
textField.becomeFirstResponder()
}
然后給Cancel按鈕以及Done按鈕添加動(dòng)作方法:
@IBAction func cancel() {
delegate?.listDetailViewControllerDidCancel(self)
}
@IBAction func done() {
if let checklist = checklistToEdit {
checklist.name = textField.text!
delegate?.listDetailViewController(self, didFinishEditing: checklist)
} else {
let checklist = Checklist(name: textField.text!)
delegate?.listDetailViewController(self, didFinishAdding: checklist)
}
}
這些代碼對(duì)你應(yīng)該非常熟悉了芭析。這和之前的編輯及添加待辦事項(xiàng)界面幾乎一模一樣衬浑。
為了在done()方法中創(chuàng)建新的Checklist對(duì)象,你使用了Checklist的init(name)方法放刨,并且將textField.text作為參數(shù)傳入到name中工秩。
你不能像下面這樣去實(shí)現(xiàn)這個(gè)目的,這樣做是達(dá)不到預(yù)期效果的:
let checklist = Checklist()
checklist.name = textField.text!
因?yàn)镃hecklist不具備一個(gè)沒有任何參數(shù)的init()方法进统,所以Checklist()會(huì)返回一個(gè)報(bào)錯(cuò)助币。它只有一個(gè)init(name)方法,所以你每次創(chuàng)建一個(gè)新的Checklist對(duì)象時(shí)螟碎,都必須用這個(gè)方法進(jìn)行初始化眉菱。
同時(shí)確保用戶無法選擇text field所在行的cell:
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
return nil
}
最后添加text field的委托方法,根據(jù)用戶的輸入是否為空來啟用或者禁用Done按鈕掉分。
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let oldText = textField.text! as NSString
let newText = oldText.replacingCharacters(in: range, with: string) as NSString
doneBarButton.isEnabled = (newText.length > 0)
return true
}
這也是你在ItemDetailViewController中做過一次的事俭缓。
讓我們?cè)诮缑娼ㄔ炱髦袨檫@個(gè)新的視圖控制器制作用戶界面。
打開故事模版酥郭,拖拽一個(gè)Navigation Controller到畫布中并且將它放置在其他視圖控制器的下面华坦。
界面建造器已經(jīng)假定你要嵌入一個(gè)table view controller到導(dǎo)航控制器中,這樣就為你省了不少事不从。
選定新的table view controller(名字叫做“root view controller”的那個(gè))并且打開身份檢查器惜姐。將class中填寫為ListDetailViewController。
將導(dǎo)航欄的標(biāo)題由“Root View Controller”修改為Add Checklist椿息。(如果雙擊不好使的話歹袁,你可以在綱要面板中選定Root View Controller然后在屬性檢查器中進(jìn)行改名)
添加Cancel和Done按鈕并且將按鈕和動(dòng)作方法鏈接起來。同時(shí)將Done按鈕和doneBarButton鏈接起來寝优,并且取消選定Enable選項(xiàng)条舔。
小貼士:如果你無法將 bar button拖拽到導(dǎo)航欄上,也可以直接往略縮面板里拖乏矾。
選中table view孟抗,然后在屬性檢查器中設(shè)置Static Cells迁杨,和style設(shè)置為Grouped。然后刪除掉多余的兩個(gè)cell夸浅。
拖拽一個(gè)Text Field到cell中帆喇,然后對(duì)其進(jìn)行如下配置:
Border Style: none
Font size: 17
Placeholder text: Name of the List
Adjust to Fit: disabled
Capitalization: Sentences
Return Key: Done
Auto-enable Return key: check
然后將這個(gè)Text Field和textField outlet鏈接起來坯钦。
然后按住ctrl將Text Field拖拽到視圖控制器上婉刀,在彈出窗口中選擇delegate突颊。這樣這個(gè)視圖控制器就是text field的委托了律秃。
打開text field的鏈接檢查器棒动,將Did End on Exit拖拽到代表視圖控制器的黃色圓圈圖標(biāo)上船惨,在彈出窗口中選擇done粱锐。
(以上步驟如果不熟悉卜范,可以回頭去看看之前的課程海雪,這些步驟我們都詳細(xì)做過一遍)
回到All Lists View Controller(就是叫做Checklists的那個(gè)),并且拖拽一個(gè)bar button上去湾宙,并且將這個(gè)button設(shè)置為Add。
按住ctrl拖拽這個(gè)新的Add按鈕到下面的導(dǎo)航控制器上埠啃,并且在彈出窗口選擇Present Modally segue碴开。
選擇這個(gè)新的轉(zhuǎn)場潦牛,并且將其命名為AddChecklist巴碗。
你的故事模版現(xiàn)在看起來應(yīng)該是這個(gè)樣子:
堅(jiān)持一下逸爵,就快完了痊银。你還需要將AllListsViewController做成ListDetailViewController的委托溯革。我們之前也做過一次類似的事情致稀。
通過在All Lists view controller的class聲明行中添加ListDetailViewControllerDelegate來使得它遵循這一協(xié)議抖单。
打開AllListsViewController.swift:
AllListsViewController: UITableViewController,ListDetailViewControllerDelegate {
還是在AllListsViewController.swift中矛绘,擴(kuò)展一下prepare(for:sender:),
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowChecklist" {
let controller = segue.destination as! ChecklistViewController
controller.checklist = sender as! Checklist
} else if segue.identifier == "AddChecklist" {
let navigationController = segue.destination as! UINavigationController
let controller = navigationController.topViewController as! ListDetailViewController
controller.delegate = self
controller.checklistToEdit = nil
}
}
第一個(gè)if中的內(nèi)容不要改動(dòng)货矮,從else if開始添加新內(nèi)容囚玫。
這段代碼的作用和以前一樣抓督,旬齋導(dǎo)航控制器中的視圖控制器铃在,并且設(shè)置它的delegate為self怔昨。
在AllListsViewController.swift的底部宿稀,添加協(xié)議方法:
func listDetailViewControllerDidCancel(_ controller: ListDetailViewController) {
dismiss(animated: true, completion: nil)
}
func listDetailViewController(_ controller: ListDetailViewController, didFinishAdding checklist: Checklist) {
let newRowIndex = lists.count
lists.append(checklist)
let indexPath = IndexPath(row: newRowIndex, section: 0)
let indexPaths = [indexPath]
tableView.insertRows(at: indexPaths, with: .automatic)
dismiss(animated: true, completion: nil)
}
func listDetailViewController(_ controller: ListDetailViewController, didFinishEditing checklist: Checklist) {
if let index = lists.index(of: checklist) {
let indexPath = IndexPath(row: index, section: 0)
if let cell = tableView.cellForRow(at: indexPath) {
cell.textLabel!.text = checklist.name
}
}
dismiss(animated: true, completion: nil)
}
這些方法會(huì)在用戶點(diǎn)擊Cancel或者Done按鈕時(shí)被調(diào)用。
這些代碼你都應(yīng)該很熟悉才對(duì)罩锐,我們之前都有完整的做過一次涩惑。
同時(shí)添加table view的數(shù)據(jù)源方法來允許用戶刪除某一條記錄:
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
lists.remove(at: indexPath.row)
let indexPaths = [indexPath]
tableView.deleteRows(at: indexPaths, with: .automatic)
}
運(yùn)行app竭恬,現(xiàn)在你可以新增或者刪除待辦事項(xiàng)分類了:
??:如果app崩潰了痊硕,那么就檢查一下是不是所有的鏈接都做好了岔绸。任何一點(diǎn)細(xì)節(jié)的丟失盒揉,都會(huì)導(dǎo)致app崩潰刚盈。
你還無法對(duì)已經(jīng)存在的條目進(jìn)行修改扁掸,然我們來完成這最后一點(diǎn)代碼。
之前我們也是通過轉(zhuǎn)場的方式進(jìn)入到編輯界面锈麸,但是這一次我們不這樣做忘伞,我們要通過手動(dòng)的方式來從故事模版中讀取這個(gè)新的視圖控制器沙兰,多掌握一些方法總是好的舀奶。
打開AllListsViewController.swift育勺,添加一個(gè)tableView(accessoryButtonTappedForRowWith)方法罗岖。這個(gè)方法是table view的委托方法之一桑包,其作用就和名字一樣一目了然哑了。
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
let navigationController = storyboard!.instantiateViewController(withIdentifier: "ListDetailNavgationController") as! UINavigationController
let controller = navigationController.topViewController as! ListDetailViewController
controller.delegate = self
let checklist = lists[indexPath.row]
controller.checklistToEdit = checklist
present(navigationController, animated: true, completion: nil)
}
在這個(gè)方法內(nèi)垒手,你為Add/Edit Checklist界面創(chuàng)建了新的視圖控制器對(duì)象科贬,并且將其展現(xiàn)在屏幕上榜掌。這和轉(zhuǎn)場的作用大致相似憎账。這個(gè)視圖控制器被嵌入到故事模版中胞皱,并且你請(qǐng)求故事模版對(duì)象讀取它九妈。
你是在哪里獲取這個(gè)故事模版對(duì)象的呢萌朱?每個(gè)視圖控制器都有一個(gè)storyboard屬性來引用這個(gè)視圖控制器是從哪個(gè)故事模版中被讀取的。你可以使用這個(gè)屬性來故事模版的所有功能翠霍,比如實(shí)例化其他視圖控制器蠢莺。
這個(gè)storyboard屬性是可選型浪秘,因?yàn)橐晥D控制器并不全部從故事模版中讀取耸携,但是我們眼下的這個(gè)是夺衍,所以我們使用感嘆號(hào)對(duì)其解包沟沙。因?yàn)槲覀兛梢源_定在我們這個(gè)app中storyboard不會(huì)為nil壁榕,所以直接用感嘆號(hào)強(qiáng)制解包就可以牌里,而不需要用if let的方式。
調(diào)用instantiateViewController(withIdentifier)時(shí)用到了一個(gè)字符串“ListDetailNavigationController”喳篇,這就是請(qǐng)求故事模版創(chuàng)建新視圖控制器的方式麸澜,在我們這個(gè)例子中炊邦,這個(gè)新的視圖控制器就是包含ListDetailViewController的導(dǎo)航控制器馁害。
你可以直接實(shí)例化ListDetailViewController裆操,但是ListDetailViewController是嵌入在導(dǎo)航控制器內(nèi)部的炉媒,如果直接實(shí)例化它而不管導(dǎo)航控制器的話吊骤,你就無法看到界面標(biāo)題,以及Done和Cancel按鈕传泊。
打開故事模版鸭巴,選擇指向List Detail View Controller的導(dǎo)航控制器鹃祖,然后打開身份檢查器校读,將Storyboard ID填寫為ListDetailNavigationController:
運(yùn)行app歉秫,點(diǎn)擊某一行上的詳細(xì)信息按鈕試試雁芙,如果app崩潰了却特,重新保存一下故事模版再運(yùn)行一次裂明。
練習(xí):設(shè)置List Detail View Controller的identifier為ListDetailNavigationController闽晦,而不是導(dǎo)航控制器笋敞,然后運(yùn)行app看看會(huì)發(fā)生什么夯巷,試著解釋一下為什么會(huì)這樣趁餐,如果你可以解釋的話后雷,那么證明你已經(jīng)掌握了這些內(nèi)容臀突。
??:你還能跟上我的步伐嗎?
如果你對(duì)這一切非常茫然并且想要放棄的話磕瓷,千萬要打消這個(gè)念頭困食。
學(xué)習(xí)新的東西本來就是一個(gè)枯燥的過程,編程尤其如此叨咖。你可以關(guān)掉電腦瘩例,去睡一覺,過幾天以后再重新打開看看甸各。
說不定就靈關(guān)一閃的明白了起來垛贤。