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

來個(gè)簡(jiǎn)短的插曲—關(guān)于函數(shù)編程

Swift首先是一種面向?qū)ο缶幊陶Z(yǔ)言,但是實(shí)際上存在一種其他風(fēng)格的編程方式并且在最近幾年也很流行,那就是函數(shù)編程。

“函數(shù)”這個(gè)術(shù)語(yǔ)的意思就是,可以純粹的通過數(shù)學(xué)函數(shù)來傳送并且改變數(shù)據(jù)。

不同于Swift中的函數(shù)或者方法,這些數(shù)據(jù)函數(shù)不允許出現(xiàn)所謂的“側(cè)面效應(yīng)”,對(duì)于任何給定的輸入,這個(gè)函數(shù)總是會(huì)產(chǎn)生一個(gè)同樣的輸出。方法則沒這么嚴(yán)格。

雖然Swift不是一種函數(shù)語(yǔ)言,它還是可以讓你在app使用一些函數(shù)編程的技術(shù)。它們會(huì)使你的代碼變的非常簡(jiǎn)潔。

作為例子,我們?cè)賮砜匆豢碿ountUncheckedItems():

func countUncheckedItems() -> Int {
  var count = 0
  for item in items where !item.checked {
count += 1 }
  return count
}

這是相當(dāng)簡(jiǎn)單的一小段代碼。但是實(shí)際上你可以把它們濃縮為一行:

func countUncheckedItems() -> Int {
  return items.reduce(0) { cnt, item in cnt + (item.checked ? 0 : 1) }
}

reduce()是一個(gè)函數(shù),作用為對(duì)每一個(gè)條目執(zhí)行花括號(hào)內(nèi)的代碼。最初變量cnt包含的值為0,但是每次執(zhí)行后它都會(huì)增加0或者1,這取決于條目是否checked。

當(dāng)reduce()結(jié)束后,它返回為被打勾的數(shù)量的總和。

你不用記住或者理解這些,但是了解一下Swift可以允許你執(zhí)行這種簡(jiǎn)潔的算法,是非常棒的。

對(duì)列表進(jìn)行排序

對(duì)列表常見的另一個(gè)操作就是以某種規(guī)則排序。

讓我們來以名稱為順序來對(duì)列表進(jìn)行排序。目前當(dāng)你添加一個(gè)Checklist的時(shí)候,新增的條目總是出現(xiàn)在最后一條,無論它的首寫字母在字母表中的順序如何。

在我們了解如何對(duì)數(shù)組進(jìn)行排序前,讓我們想想什么時(shí)候需要執(zhí)行排序:

1、新增的時(shí)候

2、重命名的時(shí)候

沒有必要在刪除掉時(shí)候進(jìn)行重新排序,因?yàn)閯h除不會(huì)引起序列的變化。

目前這兩種操作都是由AllListsViewController中的 “didFinishAdding” 和“didFinishEditing”來執(zhí)行。

將這兩個(gè)方改動(dòng)一下:

func listDetailViewController(_ controller: ListDetailViewController, didFinishAdding checklist: Checklist) {
        dataModel.lists.append(checklist)
        dataModel.sortChecklists()
        tableView.reloadData()
        dismiss(animated: true, completion: nil)
    }
    
    func listDetailViewController(_ controller: ListDetailViewController, didFinishEditing checklist: Checklist) {
        dataModel.sortChecklists()
        tableView.reloadData()
        dismiss(animated: true, completion: nil)
    }

在這兩個(gè)方法中你刪掉了不少東西,因?yàn)楝F(xiàn)在你總是在table view上使用reloadData()。

它再也不用手動(dòng)插入一個(gè)新行了,或者更新cell的textLabel。而是簡(jiǎn)單的通過調(diào)用tableView.reloadData()來刷新整個(gè)列表的內(nèi)容。

你再一次使用了全量更新,因?yàn)槲覀冞@個(gè)app中不太可能會(huì)出現(xiàn)大量數(shù)據(jù)。如果我們的列表中會(huì)出現(xiàn)上千行,那么用更加科學(xué)的方法才會(huì)顯得比較有必要。(對(duì)于大量數(shù)據(jù),你可以定位新增或者重命名的是哪一行,然后僅僅對(duì)這一行進(jìn)行更新)

DataModel中的sortChecklists()是一個(gè)新的方法,你還沒有添加它。在這之前,我們來討論下排序是如何實(shí)現(xiàn)的。

當(dāng)你對(duì)一個(gè)列表中的條目進(jìn)行排序時(shí),app會(huì)逐條比較來找出每一條合適的位置,但是比較兩個(gè)Checklist對(duì)象是什么意思呢?

在我們的app中,很顯然,我們想要根據(jù)名稱進(jìn)行排序,但是我們需要一些方法來告訴app我們的意圖。

在DataModel.swift中添加以下方法:

func sortChecklists() {
        lists.sort(by: {
            checklist1, checklist2 in
            return checklist1.name.localizedStandardCompare(checklist2.name) == .orderedAscending
        })
    }

這里你告訴lists數(shù)組Checklists的內(nèi)容要以某種準(zhǔn)則排序。

這個(gè)準(zhǔn)則由閉包提供?;ɡㄌ?hào)內(nèi)就是排序的代碼;它們以閉包的形式存在:

lists.sort(by: { /* 這里就是排序代碼 */ })

在Bull's Eye這個(gè)課程中,我們簡(jiǎn)單的介紹過一次閉包。它們包裹一段代碼到一個(gè)匿名的,內(nèi)聯(lián)的方法中。

這個(gè)閉包的作用是判斷一個(gè)Checklist對(duì)象是否應(yīng)該排在另一個(gè)之前,基于你提供的排序規(guī)則。

這個(gè)排序算法會(huì)通過閉包中的準(zhǔn)則重復(fù)的兩兩比較列表中的Checklist對(duì)象,直到數(shù)組被排序完畢。

如果你想以其他標(biāo)準(zhǔn)來進(jìn)行排序,那么你就要修改閉包中的代碼。

實(shí)際的排序準(zhǔn)則是這個(gè):

 checklist1.name.localizedStandardCompare(checklist2.name) ==
                                                       .orderedAscending

比較兩個(gè)Checklist對(duì)象,你只需要知道它們的名字。

localizedStandardCompare() 方法會(huì)結(jié)合區(qū)域中的規(guī)則比較兩個(gè)字符串,比較時(shí)會(huì)忽略大小寫(它會(huì)認(rèn)為A和a是一樣的)。

區(qū)域是一個(gè)對(duì)象,它知道一個(gè)國(guó)家中的語(yǔ)法細(xì)節(jié),比如Sorting在德語(yǔ)中的意思可能和英語(yǔ)中不太一樣。

這就是對(duì)一個(gè)數(shù)組進(jìn)行排序的全部工作:調(diào)用sort()并且給它一個(gè)閉包,閉包中包含著比較兩個(gè)Checklist對(duì)象的邏輯。

為了確保以存在的條目也被同樣的規(guī)則排序,你需要在plist文件被加載時(shí)也調(diào)用sortChecklists()方法:

func loadChecklists() {
        ...
            sortChecklists()
        }
    }

運(yùn)行app并且新增幾個(gè)待辦事項(xiàng)分類。改變它們的名稱,觀察下是否已經(jīng)按照名稱排序了。

已排序的列表

給待辦分類添加圖標(biāo)

因?yàn)檎嬲膇OS開發(fā)者不能無限制的添加視圖控制器和委托,所以我們要通過給Checklist對(duì)象添加一個(gè)新屬性的方法來增加圖標(biāo)。我們要把這些概念固化在你的腦袋里。

當(dāng)你完成后,編輯和新增待辦事項(xiàng)分類的界面會(huì)看起來是這個(gè)樣子:

你可以為分類選擇一個(gè)圖標(biāo)

當(dāng)你新增或者編輯待辦事項(xiàng)分類的時(shí)候,可以通過當(dāng)前界面打開一個(gè)新的界面來選擇圖標(biāo)。這個(gè)圖標(biāo)選擇器是一個(gè)新的視圖控制器。這一次你不會(huì)使用modally轉(zhuǎn)場(chǎng),而是會(huì)將它推入導(dǎo)航器的棧堆。

可以在課程附帶的Resources文件里找到相關(guān)的圖片文件,文件夾的名字是Checklist Icons(小伙伴們可以通過購(gòu)買正版圖書得到這個(gè)附件,也可以去網(wǎng)上下載一些自己喜歡的素材圖標(biāo)來練習(xí))

各種不同類型的圖標(biāo)

把圖標(biāo)從這個(gè)文件夾添加到Asset Catalog里。在工程導(dǎo)航起上選擇Assets.xcassets,點(diǎn)擊底部的加號(hào)按鈕,并且選擇Import

加號(hào)按鈕在這里

在文件選擇時(shí)打開Checklist Icons目錄,全選其中的文件。

全選文件,而不是選擇目錄

注意:一定要選擇文件,而不是目錄。

然后點(diǎn)擊Open,導(dǎo)入就完成了。

導(dǎo)入成功效果圖

每一個(gè)圖標(biāo)都有對(duì)應(yīng)Retina設(shè)備的2x版本和對(duì)應(yīng)Retina HD的3x版本。

就像我在之前的課程中說過的一樣,你不需要1x的低分辨率圖標(biāo),所有的iPhone,ipad或者iPod,只要能運(yùn)行iOS 10,就一定是2x或者3x分辨率。

打開Checklist.swift,添加一條屬性:

var iconName: String

變量iconName存儲(chǔ)圖標(biāo)的文件名。

擴(kuò)展一下init?(coder)和encode(with),讓它們可以分別在Checklist.plist文件中讀取和存儲(chǔ)這個(gè)圖標(biāo)名稱。

required init?(coder aDecoder: NSCoder) {
        name = aDecoder.decodeObject(forKey: "Name") as! String
        items = aDecoder.decodeObject(forKey: "Items") as! [ChecklistItem]
        iconName = aDecoder.decodeObject(forKey: "IconName") as! String
        super.init()
    }
    
    func encode(with aCoder: NSCoder) {
        aCoder.encode(name, forKey: "Name")
        aCoder.encode(items, forKey: "Items")
        aCoder.encode(iconName, forKey: "IconName")
    }

假如你以后想要自己給這個(gè)app擴(kuò)展什么功能的話,記住這一點(diǎn),你要把所有涉及到的新的屬性都添加到Checklist中,否則app就不會(huì)從plist文件中讀取或者存儲(chǔ)它們。

Xcode現(xiàn)在仍然有所不滿,它在抱怨init(name)方法中沒有這個(gè)新的屬性,報(bào)錯(cuò)信息為:Property self.iconName is not initialized at super.init call

這個(gè)意思是,如果Checklist對(duì)象用init(name)初始化,而不是用init?(coder)初始化時(shí),iconName沒有值。

更新一下init(name)方法:

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

這樣會(huì)給所有新的checklist一個(gè)“ Appointments”圖標(biāo)。

我猜你現(xiàn)在一定很想知道如何在table view上展示這個(gè)圖標(biāo)。不過在這之后,你還有更多的問題要操心,比如如何使用戶選擇圖標(biāo)。

打開AllListsViewController.swift,把圖標(biāo)放入cell中:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        ...
        cell.imageView!.image = UIImage(named: checklist.iconName)
        
        return cell
    }

使用標(biāo)準(zhǔn)cell的.subtitle風(fēng)格時(shí),會(huì)自帶一個(gè)內(nèi)建的UIImageView在左邊。你可以簡(jiǎn)單的將圖片給它,然后它會(huì)自動(dòng)將圖片展現(xiàn)出來,非常簡(jiǎn)單。

在重新運(yùn)行app前,刪除掉Checklist.plist文件,然后重置模擬器,因?yàn)槲覀兊奈募Y(jié)構(gòu)又發(fā)生了變化,如果不這樣做,app就隨時(shí)會(huì)死給你看。

運(yùn)行app,現(xiàn)在每個(gè)待辦分類都有了一個(gè)鬧鐘樣子的圖標(biāo):

圖標(biāo)展示成功

改造非常成功,你現(xiàn)在可以改變一下Checklist的init(name)方法,給每個(gè)Checklist對(duì)象一個(gè)“No Icon”圖標(biāo)作為默認(rèn)。

打開Checklist.swift,在init(name)內(nèi),稍微改動(dòng)一下:

iconName = "No Icon"

"No Icon"是個(gè)完全透明的PNG圖片,它和其他圖片的大小一模一樣。使用透明圖片的必要性在于,這樣可以使所有的行格式一樣,即使用戶有時(shí)不想要一個(gè)圖片。

如果我們僅僅是把iconName設(shè)置為空的話,那么圖片位置就不會(huì)顯示出來,標(biāo)簽會(huì)位于最左邊,看起來非常難看,大家可以比較一下沒有圖片和透明圖片的區(qū)別:

左邊為沒有圖片,右邊為透明圖片

讓我們來創(chuàng)建圖片選擇界面:

添加一個(gè)Swift文件到工程中,取名為IconPickerViewController。

將該文件中的預(yù)置內(nèi)容全部刪掉,替換為以下代碼:

protocol IconPickerViewControllerDelegate: class {
    func iconPicker(_ picker: IconPickerViewController,didPick iconName: String)
}

class IconPickerViewController: UITableViewController {
    weak var delegate: IconPickerViewControllerDelegate?
}

這里定義了IconPickerViewController對(duì)象,它是一個(gè)table view controller,并且有有一個(gè)用來和這個(gè)app中其他對(duì)象通信的協(xié)議。

在class內(nèi)部,新增一個(gè)常量數(shù)組來保存各種圖片的名稱:

let icons = ["No Icon",
                 "Appointments",
                 "Birthdays",
                 "Chores",
                 "Drinks",
                 "Folder",
                 "Groceries",
                 "Inbox",
                 "Photos",
                 "Trips"]

這是一個(gè)包含圖片名稱的常量數(shù)組。這些字符串同時(shí)也是asset catalog中PNG文件的名稱。

這個(gè)數(shù)組就是這個(gè)視圖控制器的數(shù)據(jù)模型。注意一下,這個(gè)數(shù)組不會(huì)發(fā)生改變,因?yàn)樗怯胠et而不是var定義的,因?yàn)橛脩舨荒茉黾踊蛘邉h除圖片。

這個(gè)新的視圖控制器是一個(gè)UITableViewController,所以你需要執(zhí)行table view的數(shù)據(jù)源方法。

在文件中添加以下方法:

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

這里簡(jiǎn)單的返回了數(shù)組中對(duì)象的數(shù)量

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "IconCell", for: indexPath)
        let iconName = icons[indexPath.row]
        cell.textLabel!.text = iconName
        cell.imageView!.image = UIImage(named: iconName)
        return cell
    }

這里你得到了一個(gè)table view cell并且給它了一個(gè)文本和圖片。稍候你會(huì)在故事模版里設(shè)計(jì)這個(gè)cell。

這是一個(gè)標(biāo)準(zhǔn)cell,風(fēng)格為“Default”(在界面建造器中對(duì)應(yīng)的名字是“Basic”)。這個(gè)風(fēng)格的cell自帶一個(gè)文本標(biāo)簽,一個(gè)圖片視圖,非常的便利。

打開故事模版。拖拽一個(gè)Table View Controller到畫布上,把它放在List Detail View Controller的旁邊(就是名字叫做“Add Checklist”)的那個(gè)。

打開這個(gè)新視圖的身份檢查器,將在class中填入IconPickerViewController。

選定cell,在屬性檢查器中設(shè)置Style(風(fēng)格)為Basic,Identifier為IconCell。

這樣,圖片選擇器的cell就設(shè)置好了?,F(xiàn)在你需要在某些地方調(diào)用它。為了實(shí)現(xiàn)這個(gè)目的,你需要在Add/Edit Checklist界面添加一個(gè)新的行。

選擇List Detail View Controller,并且在table view中添加一個(gè)新的分節(jié)。你可以在table view的屬性檢查器中將Sections設(shè)置為2。這樣就有兩個(gè)分節(jié)了。

刪除掉新cell上的Text Field,你并不需要它。

新增一個(gè)標(biāo)簽到這個(gè)cell上,并且命名為Icon。

設(shè)置這個(gè)cell的Accessory為Disclosure Indicator,這樣你會(huì)得到一個(gè)灰色的大于號(hào)的按鈕。

添加一個(gè)Image View到這個(gè)cell的右邊,大小為36*36(可以使用尺寸檢查器來設(shè)置)

??:如果你的Mac OS版本為Sierra,Xcode中的Image View也許會(huì)展現(xiàn)為一個(gè)白色的矩形,非常難以觀察。我認(rèn)為這是Xcode的一個(gè)bug,在EI Capitan版本的Mac OS上,Image View就展現(xiàn)為一個(gè)藍(lán)色的矩形。

打開輔助編輯器為這個(gè)image view添加一個(gè)outlet,命名為iconImageView。(輔助編輯器中的文件應(yīng)該是在ListDetailViewController.swift)

這時(shí),兩個(gè)界面的設(shè)計(jì)都完成了,你現(xiàn)在可以使用轉(zhuǎn)場(chǎng)把它們連接起來了。

按住ctrl拖拽這個(gè)新的table view cell到Icon Picker View Controller并且將轉(zhuǎn)場(chǎng)的類型設(shè)置為Show。(確保你拖拽的是table view cell,而不是其中的圖片或者標(biāo)簽)

將這個(gè)新的轉(zhuǎn)場(chǎng)的identifier設(shè)置為PickIcon。

托轉(zhuǎn)場(chǎng)的福,這個(gè)新的視圖控制器有了一個(gè)導(dǎo)航欄。雙擊導(dǎo)航欄,將其標(biāo)題命名為Choose Icon。

??:如果雙擊無法修改導(dǎo)航欄標(biāo)題,你可以先拖拽一個(gè)Navigation Item到這個(gè)新的視圖控制器上。然后就可以修改標(biāo)題了,Xcode也是存在一些bug的。

現(xiàn)在你的故事模版中的這一部分,看起來應(yīng)該是這個(gè)樣子:

完成圖

打開ListDetailViewController.swift,將table view的willSelectRowAt委托方法修改一下:

override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
        if indexPath.section == 1 {
            return indexPath
        } else {
            return nil
        }
    }

如果不這樣做的話,點(diǎn)擊“Icon”cell,就不會(huì)觸發(fā)轉(zhuǎn)場(chǎng)。

之前這個(gè)方法總是返回nil,就是說不可能對(duì)這一行進(jìn)行點(diǎn)擊。現(xiàn)在你必須允許用戶點(diǎn)擊“Icon”cell所在對(duì)行,所以當(dāng)點(diǎn)擊這一行時(shí),需要返回它的indexPath。

因?yàn)椤癐con”cell是第二個(gè)分節(jié)中的一行,所以你要查看一下indexPath.section,這里并不需要檢查行號(hào),因?yàn)榈诙€(gè)分節(jié)只有一行。用戶仍然不能選擇第一個(gè)分節(jié)中的行。

運(yùn)行app,確認(rèn)一下,在添加和編輯Checklist的界面有一行是用于選擇圖標(biāo)的,并且點(diǎn)擊這一行后會(huì)展示圖標(biāo)的列表。

像這樣就對(duì)了

你可以點(diǎn)擊back按鈕來回到上一步,但是選擇圖標(biāo)功能還不會(huì)生效,你點(diǎn)擊一個(gè)圖標(biāo),僅僅是可以看到這一行變灰了,表示已經(jīng)被選中。

為了完成剩下的部分,你需要使用icon picker的委托協(xié)議把它和Checklist連接起來。

首先,在ListDetailViewController.swift中添加一個(gè)實(shí)例變量:

var iconName = "Folder"

你使用這個(gè)變量來跟蹤被選擇的圖標(biāo)名稱。

因?yàn)榧词笴hecklist對(duì)象已經(jīng)有了一個(gè)iconName屬性,你也不能保證百分百的追蹤到被選擇圖標(biāo)的名稱,原因很簡(jiǎn)單,因?yàn)橛锌赡艽藭r(shí)Checklist對(duì)象根本不存在,比如正在新建一個(gè)Checklist的時(shí)候。

所以你要把圖標(biāo)的名稱存儲(chǔ)到一個(gè)臨時(shí)變量里,并且在合適的時(shí)候把這個(gè)臨時(shí)變量的值拷貝到Checklist的iconName屬性中。

你要在合理的對(duì)iconName變量進(jìn)行初始化。僅對(duì)新建Checklist對(duì)象時(shí)需要這樣做,我們需要在新建Checklist對(duì)象時(shí),給它一個(gè)默認(rèn)圖標(biāo),就用文件夾圖標(biāo)好了。

改動(dòng)一下viewDidLoad()方法:

override func viewDidLoad() {
        super.viewDidLoad()
        
        if let checklist = checklistToEdit {
            title = "Edit Checklist"
            textField.text = checklist.name
            doneBarButton.isEnabled = true
            iconName = checklist.iconName    //添加這一行
        }
        iconImageView.image = UIImage(named: iconName)   //添加這一行
    }

這里新增了兩行,如果checklistToEdit不為nil,那么你將Checklist對(duì)象的iconName拷貝到新建的iconName變量中,你同時(shí)還讀取圖標(biāo)的圖片文件到一個(gè)新的UIImage對(duì)象中,并且將它設(shè)置到iconImageView中,這樣圖標(biāo)就可以展示出來了。

之前你創(chuàng)建的轉(zhuǎn)場(chǎng)名稱叫做“PickIcon”,你還需要執(zhí)行prepare(for:sender:)來告訴IconPickerViewController這個(gè)界面是它的委托。

打開ListDetailViewController.swift,添加下面的方法:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "PickIcon" {
            let controller = segue.destination as! IconPickerViewController
            controller.delegate = self
        }
    }

這對(duì)你應(yīng)該很輕車熟路了。

當(dāng)然,Xcode已經(jīng)發(fā)現(xiàn)了有些事情不對(duì),并且給出了一個(gè)大大的警告:在“controller.delegate = self”這一行。

Xcode的意思是:“Cannot assign a value of type 'ListDetailViewController' to a value of type
'IconPickerViewControllerDelegate?'”(不能把類型ListDetailViewController的值分配給IconPickerViewControllerDelegate)

練習(xí):我們忘記了什么?

答案:你還沒有讓這個(gè)視圖控制器遵循委托協(xié)議,所以Swift不會(huì)使ListDetailViewController成為icon picker的委托。

在class這一行添加一點(diǎn)東西進(jìn)去:

class ListDetailViewController: UITableViewController,UITextFieldDelegate,IconPickerViewControllerDelegate

并且執(zhí)行委托協(xié)議中的方法,隨便放在什么地方都可以,但是要在ListDetailViewController class的里面:

func iconPicker(_ picker: IconPickerViewController, didPick iconName: String) {
        self.iconName = iconName
        iconImageView.image = UIImage(named: iconName)
        let _ = navigationController?.popViewController(animated: true)
    }

這里將被選擇的圖標(biāo)名稱存放到iconName變量中,并且用新的圖片更新了image view。

這里不能調(diào)用dismiss()而是調(diào)用popViewController(animated),因?yàn)镮con Picker位于導(dǎo)航棧堆中。當(dāng)創(chuàng)建這個(gè)轉(zhuǎn)場(chǎng)的時(shí)候你使用了Show而不是present modally,所以這里要使用“pop”,dismiss()僅用于present modally。

回憶一下,navigationController是這個(gè)視圖控制器的一個(gè)可選型的屬性,所以你使用問號(hào)或者感嘆號(hào)來解包得到實(shí)際的UINavigationController對(duì)象。通過let _ =你告訴了Xcode不需要關(guān)心popViewController()的返回結(jié)果。不這樣做的話,Xcode又會(huì)給出一個(gè)警告。這里的下劃線符號(hào)叫做通配符,你可以用具體的變量名稱來替換它。

??:你已經(jīng)知道了self用于引用對(duì)象自己。這里你寫的:
self.iconName = iconName
理由是這里iconName可以指代兩個(gè)不同的東西:1、委托方法的參數(shù)。2、實(shí)例變量。
為了消除歧義,你用帶有self前綴的來表示實(shí)例變量,這樣編譯器就很清楚你想要用的是這兩個(gè)iconName中的那一個(gè)。

改變一下done()方法,以便于將被選擇的icon name放入Checklist對(duì)象。

@IBAction func done() {
        if let checklist = checklistToEdit {
            checklist.name = textField.text!
            checklist.iconName = iconName  //添加這一行
            delegate?.listDetailViewController(self, didFinishEditing: checklist)
        } else {
            let checklist = Checklist(name: textField.text!)
            checklist.iconName = iconName  //添加這一行
            delegate?.listDetailViewController(self, didFinishAdding: checklist)
        }
    }

最后,你必須改變IconPickerViewController,使它在某一行被點(diǎn)擊時(shí),實(shí)際的調(diào)用委托方法。

打開IconPickerViewController.swift,在底部添加一個(gè)方法:

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if let delegate = delegate {
            let iconName = icons[indexPath.row]
            delegate.iconPicker(self, didPick: iconName)
        }
    }

現(xiàn)在你就完成了對(duì)Checklist對(duì)象圖標(biāo)設(shè)置的工作。

回顧一下,你做了以下事情:

1、添加一個(gè)新的視圖控制器

2、在故事模版中設(shè)計(jì)界面

3、使用轉(zhuǎn)場(chǎng)和委托將這個(gè)視圖控制和添加及編輯Checklist界面連接起來。

當(dāng)你每新建一個(gè)視圖控制的時(shí)候,都需要完成以上基本步驟。

運(yùn)行app,試試效果如何:

最后編輯于
?著作權(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)容