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

給待辦事項(xiàng)分類

這個(gè)app的名稱叫做Checklists是有原因的:它允許你擁有多條待辦分類,目前為止,我們都只能添加待辦條目,但是不久之后你就會(huì)擁有添加分類的功能,你可以先增加一些分類,比如會(huì)議,日程等分類,然后再向每個(gè)分類下增加具體的待辦項(xiàng)目。

我們要做以下幾件事情:

1、添加一個(gè)新的界面展示分類。

2、創(chuàng)建一個(gè)新的界面,可以使用戶添加或者編輯新的分類

3、當(dāng)你點(diǎn)擊具體一個(gè)分類時(shí),顯示其中的待辦事項(xiàng)

4、對(duì)分類進(jìn)行保存和讀取

兩個(gè)新的界面意味著我們要兩個(gè)新的視圖控制器。

1、AllListsViewController展示用戶所有的分類。

2、ListDetailViewController,使用戶可以添加,編輯分類以及為分類增加一個(gè)圖標(biāo)

首先你要添加的是AllListsViewController。這也是這個(gè)app新的主界面。

當(dāng)你完成后,app看起來會(huì)是這個(gè)樣子:

新的app主界面

這個(gè)界面和你之前創(chuàng)建的界面非常相似。它也是一個(gè)table view controller。

從現(xiàn)在開始,我會(huì)將這個(gè)新的主界面稱為“分類”界面,而將之前的展示待辦事項(xiàng)列表的界面稱為“事項(xiàng)”界面,以示區(qū)別。

在工程導(dǎo)航器中右擊Checklists分組(黃色文件夾圖標(biāo)的那個(gè)),然后選擇New File,選擇Cocoa Touch Class模版(iOS標(biāo)簽下的)。

然后按照下面的示例選擇選項(xiàng):

Class:AllListsViewController

Subclass of:UITableViewController

Also create XIB file:Uncheck this(不要勾選)

Language:Swift

新建AllListsViewController文件

注意:確保Subclass of這一欄填寫的是UITableViewController,而不是UIViewController。同時(shí)注意一下,Xcode會(huì)自動(dòng)將AllListsViewController重命名為AllListsTableViewController,多了一個(gè)Table,你需要稍微修改一下。

點(diǎn)擊Next,然后點(diǎn)擊Create提交。

Xcode的table view controller模版中會(huì)預(yù)置一些代碼,但是也許你用不到它們。模版只是把認(rèn)為你需要的東西都提前列了出來,所以首先我們要把它們都刪干凈。

你還是先要自己造一點(diǎn)數(shù)據(jù),讓app能先跑起來。和你知道的一樣,我每做一小步都會(huì)運(yùn)行app測(cè)試一下,如果運(yùn)行效果正常,那么就進(jìn)入到下一步。

打開AllListsViewController.swift,刪除掉numberOfSections(in)方法。沒有這個(gè)方法的時(shí)候,列表就只會(huì)有一個(gè)分節(jié)。

將tableView(numberOfRowsInSection)修改為:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 3
    }

接下來修改tableView(cellForRowAt),注意一下,這個(gè)方法在文件里有,只是被注釋掉了,你可以把注釋取消掉就可以了。并且修改為下面這個(gè)樣子:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = makeCell(for: tableView)
        cell.textLabel!.text = "List \(indexPath.row)"
        return cell
    }

在ChecklistViewController中你是在界面建造器中設(shè)計(jì)cell單元,但是在AllListsViewController中,我們使用另外一種方式,用代碼來設(shè)計(jì)cell單元。

你需要根據(jù)編譯器的提示添加下面這個(gè)方法:

func makeCell(for tableView: UITableView) -> UITableViewCell {
        let cellIdentifier = "Cell"
        if let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier) {
            return cell
        } else {
            return UITableViewCell(style: .default, reuseIdentifier: cellIdentifier)
        }
    }

一會(huì)我會(huì)詳細(xì)的講這些代碼的作用,但是現(xiàn)在你要知道你在這里也使用了tableView.dequeueReusableCell(withIdentifier)。如果它返回nil,那么就是說沒有可重用的cell,這時(shí)你要使用UITableViewCell(style, reuseIdentifier)來新建一個(gè)。

把這段代碼單獨(dú)分離出來后會(huì)起到保持tableView(cellForRowAt)簡(jiǎn)潔的作用。

把AllListsViewController.swift中的其他注釋部分全部刪掉,它們沒有任何作用,只會(huì)讓代碼看起來亂糟糟的。

最后一步就是添加新的view controller到故事模版上。

打開故事模版并且拖拽一個(gè)新的Table View Controller到畫布上,把它放到離第一個(gè)navigation controller近的地方。

按住ctrl并且從第一個(gè)navigation controller拖拽到這個(gè)新的視圖控制器:

在彈出的菜單上選擇Relationship Segue分節(jié)下的root view controller:

選擇root view controller

這樣會(huì)把前期存在的navigation controller和ChecklistViewController之間的鏈接斷開,這樣一來,Checklists就不再是主界面了。

選擇新的這個(gè)table view controller并且打開身份檢查器,在Class中輸入AllListsViewController。

雙擊這個(gè)新的視圖控制器的導(dǎo)航欄,并且將它重命名為Checklists。

這樣在Xcode的略縮面板中All Lists View Controller的視圖控制器會(huì)被重命名為Checklists,這可能會(huì)使你有些困惑,因?yàn)槲覀円呀?jīng)有一個(gè)Checklists了,我們稍后會(huì)處理這個(gè)問題。

你可以整理一下故事模版,調(diào)整下新視圖控制器的位置,讓它們看起來美觀點(diǎn),把它們放到一排去。

就像我之前提到過的,你在這里不要為這個(gè)table view使用標(biāo)準(zhǔn)cell單元,如果你已經(jīng)用了,那么非常不錯(cuò),并且作為一個(gè)練習(xí)你可以之后重寫代碼來使用標(biāo)準(zhǔn)cell單元,但是這次我要為你展示一個(gè)新的方法來生成table view cells。

把All Lists View Controller中的空的prototype cell刪掉,選定以后按delete鍵就可以了。

然后選定視圖控制(黃色圓圈圖標(biāo)按鈕的那個(gè)),然后按住ctrl鍵拖拽到Checklist View Controller中去,創(chuàng)建一個(gè)Show轉(zhuǎn)場(chǎng)。

這樣就在從All Lists界面到Checklist界面見加入了一個(gè)推入的轉(zhuǎn)換。同時(shí)也把右邊的導(dǎo)航欄放回到Checklist界面。

雙擊右邊的導(dǎo)航欄,修改標(biāo)題為Name of the Checklist。這只是預(yù)置的文本,它可以幫你區(qū)分略縮面板中的各個(gè)視圖控制器。

??:略縮面板中不顯示視圖控制器對(duì)象的名稱,而只是顯示導(dǎo)航欄的文本,這一點(diǎn)Xcode需要改進(jìn),否則非常容易把人弄暈。
當(dāng)我們說到All Lists View Controller時(shí),就是指略縮面板中的Checklists Scene。
而Checklist View Controller現(xiàn)在則是略縮面板中的Name of the Checklist Scene。

注意一下,這個(gè)轉(zhuǎn)場(chǎng)沒有和任何按鈕或者table view cell關(guān)聯(lián)。

并且在All Lists界面上也沒有任何東西給你點(diǎn)擊,換而言之就是說你無法觸發(fā)這個(gè)轉(zhuǎn)場(chǎng)。這就意味著你要通過編程的方式來觸發(fā)它。

選定新的轉(zhuǎn)場(chǎng),并且打開屬性檢查器,在identifier輸入ShowChecklist。

這里你還可以看到這個(gè)轉(zhuǎn)場(chǎng)的Kind(類型)為Show (e.g.Push),因?yàn)閳?zhí)行這個(gè)轉(zhuǎn)場(chǎng)時(shí),你正在將Checklist View Controller推到導(dǎo)航層的上面。

打開AllListsViewController.swift,添加tableView(didSelectRowAt)方法:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        performSegue(withIdentifier: "ShowChecklist", sender: nil)
    }

回憶一下,這個(gè)table view委托方法是當(dāng)用戶點(diǎn)擊某一行的時(shí)候被觸發(fā)。

之前,點(diǎn)擊某一行時(shí),會(huì)自動(dòng)觸發(fā)一個(gè)轉(zhuǎn)場(chǎng),因?yàn)槟銓⑥D(zhuǎn)場(chǎng)和cell單元鏈接起來了。然而,這個(gè)新的table view并沒有使用cell單元,因此你需要手動(dòng)執(zhí)行轉(zhuǎn)場(chǎng)。

這非常簡(jiǎn)單:只需要調(diào)用performSegue(withIdentifier,sender)就可以了。

運(yùn)行app,它看起來應(yīng)該是這個(gè)樣子:

左邊是第一個(gè)界面,右邊是點(diǎn)擊某一行后進(jìn)入的界面

點(diǎn)擊某一行后我們非常熟悉的ChecklistViewController就滑動(dòng)到屏幕中了。

你可以點(diǎn)擊導(dǎo)航欄上左邊的“Back”回到主界面上?,F(xiàn)在你應(yīng)該真正體會(huì)到導(dǎo)航控制器的強(qiáng)大了。

在All Lists界面中放入內(nèi)容

你將要把Checklist View Controller中的大部分功能復(fù)制到All Lists界面中。

這里將會(huì)有一個(gè)?號(hào)按鈕用戶添加新的分類,可以通過滑動(dòng)刪除某個(gè)分類,可以通過點(diǎn)擊詳細(xì)信息按鈕來編輯某個(gè)分類。

當(dāng)然,你還要將分類對(duì)象保存到一個(gè)plist文件中。

因?yàn)橹斑@些步驟我們已經(jīng)詳細(xì)講解過了,所以這次我們會(huì)將的稍微快一點(diǎn)。

我們首先要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)模型來代表分類,就叫做Checklist好了,之前的代表具體某條待辦事項(xiàng)的數(shù)據(jù)模型叫做ChecklistItem。

用Cocoa Touch Class模版添加一個(gè)新的文件,將其命名為Checklist并且設(shè)置為NSObject的子類。

就像ChecklistItem一樣,你需要把Checklist作為NSObject的子類,因?yàn)镹SCoder系統(tǒng)在存儲(chǔ)和讀取時(shí),對(duì)象必須是這種類型。

給Checklist.swift一個(gè)叫做name的屬性:

import UIKit

class Checklist: NSObject {
    var name = ""
}

接下來,你需要一個(gè)數(shù)組來保存AllLIstsViewController的Checklist對(duì)象。

在AllListsViewController.swift中添加一個(gè)新的實(shí)例變量。

var lists: [Checklist]

這個(gè)數(shù)組就用于存儲(chǔ)Checklist對(duì)象。

??:你也可以這樣聲明數(shù)組:
var lists: Array<Checklist>
在Swift代碼中你會(huì)見到這兩種聲明方式,它們的作用是一樣的。

你可以給這個(gè)新的數(shù)組添加一點(diǎn)測(cè)試數(shù)據(jù),可以通過init?(coder)來實(shí)現(xiàn)這一目的。記住當(dāng)UIKit從故事模版中讀取視圖控制器時(shí)會(huì)自動(dòng)調(diào)用這個(gè)方法。

打開AllListsViewController.swift,像下面這樣就可以實(shí)現(xiàn)了(先不要急著動(dòng)手去做,先閱讀一遍,當(dāng)需要你敲代碼的時(shí)候,我會(huì)通知你的)

required init?(coder aDecoder: NSCoder) {
  // 1
  lists = [Checklist]()
  // 2
  super.init(coder: aDecoder)
// 3
  var list = Checklist()
list.name = "Birthdays"
  lists.append(list)
// 4
  list = Checklist()
  list.name = "Groceries"
  lists.append(list)
  list = Checklist()
  list.name = "Cool Apps"
  lists.append(list)
  list = Checklist()
  list.name = "To Do"
  lists.append(list)
}

這和你在ChecklistViewController中添加測(cè)試數(shù)據(jù)的方法非常相似。下面是每一步的講解。

1、給lists變量一個(gè)值。你也可以寫成lists= Array<Checklist>(),我比較喜歡方括號(hào)的那個(gè)版本。

2、調(diào)用init?(coder)父類的初始化方法。沒有這一步,就不能從故事模版中讀取出這個(gè)視圖來。但是你也不用太擔(dān)心,如果你真的忘了這個(gè)步驟,Xcode會(huì)提醒你的。

創(chuàng)建一個(gè)新的Checklist對(duì)象,給它一個(gè)名稱,并且將它添加到數(shù)組中。

4、重復(fù)創(chuàng)建多個(gè)Checklist對(duì)象。因?yàn)閘ist是一個(gè)變量,所以你可以復(fù)用它。

注意一下,你每次創(chuàng)建一個(gè)新的Checklist對(duì)象,都重復(fù)了同樣的兩個(gè)步驟:

 list = Checklist()
list.name = "Name of the check

看起來每一個(gè)你創(chuàng)建的Checklist對(duì)象都有一個(gè)名稱。你可以通過自己寫一個(gè)init方法,將name作為一個(gè)參數(shù),然后你就可以將這兩行合并為一行了,就像下面這樣:

list = Checklist(name: "Name of the checklist")

打開Checklist.swift并且添加新的init方法:

init(name: String) {
        self.name = name
        super.init()
    }

這個(gè)初始化用了一個(gè)參數(shù)name,并且將它傳遞給實(shí)例變量name。

因?yàn)閰?shù)名稱和實(shí)例變量都叫做name,所以你使用self.name來引用實(shí)例變量。

如果你像下面這樣寫代碼:

init(name: String) {
        name = name
        super.init()
    }

編譯器就會(huì)死給你看,因?yàn)樗植磺迥莻€(gè)name是參數(shù),而哪個(gè)name是實(shí)例變量了。

為了消除歧義,你在實(shí)例變量name前加了self.回憶一下,self引用你當(dāng)前所處的對(duì)象,所以self.name就代表Checklist中的實(shí)例變量name。

回到AllListsViewController.swift,然后添加init?(coder)方法,這次你要?jiǎng)邮秩プ隽恕?/p>

required init?(coder aDecoder: NSCoder) {
        lists = [Checklist]()
        
        super.init(coder: aDecoder)
        
        var list = Checklist(name: "Birthdays")
        lists.append(list)
        
        list = Checklist(name: "Groceries")
        lists.append(list)
        
        list = Checklist(name: "Cool Apps")
        lists.append(list)
        
        list = Checklist(name: "To Do")
        lists.append(list)
    }

這比最初我展示給你看的那一版簡(jiǎn)單了許多,并且它保證了新的Checklist對(duì)象的name屬性總是有值的。

注意一下,你不會(huì)寫成下面這個(gè)樣子:

var list = Checklist.init(name: "Birthdays")

雖然方法的名稱叫做init,但是它不是標(biāo)準(zhǔn)方法。你在使用初始化方法的時(shí)候只需要像下面這樣寫:

var object = ObjectName(parameter1: value1, parameter2: value2, . . .)

根據(jù)你指定的參數(shù),Swift會(huì)自動(dòng)找到相應(yīng)的init方法。

明白了嗎?我們進(jìn)入到下一步吧。

將tableView(numberOfRowsInSection)修改為下面這個(gè)樣子:

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return lists.count
    }

然后修改tableView(cellForRowAt),來在cell中填進(jìn)剛才的數(shù)據(jù):

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = makeCell(for: tableView)
        
        let checklist = lists[indexPath.row]
        cell.textLabel!.text = checklist.name
        cell.accessoryType = .detailDisclosureButton

運(yùn)行app,看起來會(huì)是這個(gè)樣子:

這個(gè)界面還有很多剩下的工作要做,這里僅僅是一個(gè)開始。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容