iOS Apprentice中文版-從0開始學(xué)iOS開發(fā)-第二十一課

制作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)題。

涉及轉(zhuǎn)場的步驟

這一系列過程解釋了為什么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ì)做過一遍)

最終的結(jié)果

回到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è)樣子:

全家福,三個(gè)導(dǎo)航控制和四個(gè)table view controller

堅(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)一閃的明白了起來垛贤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市趣倾,隨后出現(xiàn)的幾起案子聘惦,更是在濱河造成了極大的恐慌,老刑警劉巖儒恋,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件善绎,死亡現(xiàn)場離奇詭異禀酱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門砖第,熙熙樓的掌柜王于貴愁眉苦臉地迎上來智听,“玉大人莉测,你說我怎么就攤上這事董朝。” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵泽腮,是天一觀的道長碧磅。 經(jīng)常有香客問我货邓,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮察署,結(jié)果婚禮上业簿,老公的妹妹穿的比我還像新娘巷燥。我一直安慰自己钝腺,他們只是感情好喷斋,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布诺核。 她就那樣靜靜地躺著管毙,像睡著了一般卓舵。 火紅的嫁衣襯著肌膚如雪恒界。 梳的紋絲不亂的頭發(fā)上虾宇,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天艰赞,我揣著相機(jī)與錄音仔役,去河邊找鬼逆皮。 笑死,一個(gè)胖子當(dāng)著我的面吹牛竹习,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼邑飒!你這毒婦竟也來了腔召?” 一聲冷哼從身側(cè)響起臀蛛,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎囱稽,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡媳叨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了关顷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糊秆。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖议双,靈堂內(nèi)的尸體忽然破棺而出痘番,到底是詐尸還是另有隱情,我是刑警寧澤平痰,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布汞舱,位于F島的核電站,受9級(jí)特大地震影響宗雇,放射性物質(zhì)發(fā)生泄漏昂芜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一赔蒲、第九天 我趴在偏房一處隱蔽的房頂上張望泌神。 院中可真熱鬧,春花似錦嘹履、人聲如沸腻扇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽幼苛。三九已至,卻和暖如春焕刮,著一層夾襖步出監(jiān)牢的瞬間舶沿,已是汗流浹背墙杯。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留括荡,地道東北人高镐。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像畸冲,于是被迫代替她去往敵國和親嫉髓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

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