讓不懂程式設計的人愛上iPhone開發系列2 iOS12+Swift4.2版-Checklists-12
說明:
本系列教程改編自raywenderlich.com中的iOS Apprentice系列,有需要的童鞋請移步到這裡購買英文版原教程: https:// store.raywenderlich.com /products/ios-apprentice
歡迎繼續我們的課程。
在上⼀篇的內容中我們成功的建立了第⼀個數據模型,雖然是⽤最蠢的辦法來實現的,但好⽍也是實現了。 但之所以說這種⽅法很蠢,是因為我們這⾥只顯⽰了5⾏資料,如果有100⾏甚⾄1000⾏資料要顯⽰呢?難不成你要分別建立1000個變數來儲存這些資料資訊? 要知道程式猿通常是最會偷懶的⼀個群體,雖然他們經常和程式碼打交道,但其實是最懶得寫程式碼的。
為了讓⾃己可以更好的偷懶,於是⼀個新的事物誕⽣了,那就是-陣列(array) 。
在這一課的內容中,我們會接觸陣列的概念,這也是我們最常接觸的“資料結構”之一~
陣列
那麼什麼是陣列呢?

不知道哪個喜歡賣弄懸殊的傢伙把array翻譯成了陣列,但千萬不要顧名思義,array(陣列)可不是數字的組合,確切的說是“物件陣列”,也就是⼀堆物件的有序排列。 當然,勉強解釋為一組資料也不算太離譜。
我們現在已經知道了變數可以⽤來儲存⼀個數值或者⼀個物件,⽽陣列則可以儲存多個數值或物件。
當然,在Swift/Objective-C中,array本⾝也是⼀個物件(也就是NSArray物件),我們可以在這個物件⾥面放入變數。還有⼀件更酷的事情,因為array本⾝也是物件,所以我們可以在array陣列中儲存其它陣列。
所以從這個角度來看,array翻譯成陣列實在是太不知趣了。
好了,吐槽這些術語翻譯也沒⽤,誰叫你不是第⼀批接觸國外電腦科學的⼈呢?說不定有人還會找出⼀大堆理由說這種翻譯很有道理很有深度呢。但新手小白肯定不會這麼認為。
這也是我為什麼⼀再強調學習程式設計最好直接看英⽂書籍、⽂檔和部落格,因為老外很少裝B。哪怕是成名已久的科學家,在寫科普類文章和教材的時候仍然是時刻把小白的需求放在⼼上。比如霍金寫的《時間簡史》,我在上初中的時候就愛不釋手,偷偷在課堂看。當然,這個也不是絕對的。

現在國內的不少科普書籍和文章也不再像以前那樣喜歡賣弄了,比如《三體中的物理學》,還真心寫的不錯。再舉個例子,相比Ian Goodfellow的《深度學習》,周志華老師的西瓜書《機器學習》同樣精彩經典而且不再晦澀難懂。

不過就目前而言,可以說至少在平均水平上,國內大多數的科普類文章和開發教程還是不如英文原版的。所以我們需要繼續努力~
廢話不多說,我們繼續學習陣列。
陣列中的元素是使⽤數字來編號的,按照程式碼世界的慣例,當然還是以0開始,而不是一般人習慣的從1開始。
比如對於某個陣列中的第⼀個物件,可以⽤array[0]表示。需要注意的是,陣列是”有序”排列的物件組合,因此物件在陣列中的排列順序是很重要的。
實際上在Swift/Objective-C中,陣列其實還是一個collection(集合)物件。除了陣列之外,還有 Dictionary和 Set也屬於集合類物件,它們的區別在於物件在集合中的排列組合方式不同。以Dictionary物件為例,⾥面所儲存的物件都是以鍵值對的形式存在。好吧,⼀不⼩心我也裝B了。 什麼是鍵值對?如果你⽤過現實⽣活中的字典就會知道,字典中的內容形式都是詞條和對詞條的解釋。也就是⼀個詞條對應⼀個解釋。鍵值對也是這個意思,⼀個主鍵對應⼀個數值。你可以把Dictionay看做⼀個物件詞典。只是⾥面的詞條和解釋都是物件。
陣列中物件的組織形式和表檢視中的⾏有些類似,它們都是物件的有序排列,因此這⾥我們可以考慮把表檢視中⾏資料的資料模型放到⼀個數組中。
陣列可以根據index序號來有序儲存物件,但現在⾏中包含兩部分資料,⽂本和選中狀態。如果我們可以讓每⼀⾏只有⼀個物件就好辦了,這樣⼀來,表檢視中的⾏編號就直接等同於陣列中的index編號。為了實現這⼀點, 我們需要把⽂本內容和勾選狀態整合到⼀個自創的新物件⾥⾯面。
動手時間到了~
開啟Xcode,在專案導航部分選中Checklists群組,然後右鍵單擊。從彈出選單中選擇New File...
然後選擇Swift File。


點選Next繼續,將新的檔案儲存為ChecklistItem(不需要手動新增.swift字尾)。
點選Create,我們就建立了一個新的檔案。

開啟ChecklistItem.swift檔案,並更改程式碼如下:
// //ChecklistItem.swift //Checklists // //Created by eseedo on 3/6/19. //Copyright © 20 19 icode. All rights reserved. // import Foundation class ChecklistItem{ var text = "" var checked = false }
好了,這其實就是在iOS中建立⼀個新物件的最簡單方法。
注意,我們這裡使用了關鍵詞class來定義物件,然後使用var來添加了兩個例項變數。
這裡的text屬性將用來儲存待辦事務的描述文字,而checked屬性則用於儲存cell是否被勾選的狀態。
細心的童鞋可能會看到我們這裡又用了兩個名詞,property(屬性)和instance variable(例項變數)。
在Swift的術語表中,屬性跟例項變數完全是一回事。
當然在Objective-C中就不太一樣了,雖然兩者很接近,但卻有所差異。
好了,這樣就⾏了。ChecklistItem物件當前的主 要作用就是將⽂本和勾選標誌合併到⼀個物件之中。
如果你對這種組合⽅式覺得有點理解不能,可以考慮下⾃己當前的投資組合(或者假裝自己有也行~)。假定你是個土豪,那麼很有可能你的資產不會全部是黃金,也不會全部是房產,更不會全部是現鈔。。。很有可能你的資產中既有黃金,還有比特幣,還有地產,還有多種外幣,還有現金,還有現在快上天的股票,還有期權,甚至還有專利和智慧財產權。
假定你的資產是⼀個數組物件MyAsset,那麼它可以有多個屬性(例項變數),或許它們根本無法用同一個單位進行衡量,但卻依然可以儲存在同一個陣列之中,而無需先強行轉化成同一種貨幣。
使用物件
在開始使⽤陣列之前,先讓我們使⽤ChecklistItem物件來替代檢視控制器中的String和BOOL例項變數吧。
在ChecklistViewController.swift中,刪除之前的屬性值定義(就是分別使用let和var定義的),然後使用ChecklistItem物件來取而代之。
var row0item = ChecklistItem() var row1item = ChecklistItem() var row2item = ChecklistItem() var row3item = ChecklistItem() var row4item = ChecklistItem()
小技巧
如果你擔心出錯,可以先使用註釋的方式把原來的程式碼註釋掉。後面新新增的程式碼萬一出錯了,還可以立即恢復可用狀態~
然鵝,這一切看起來似乎非常的怪異。
之前的程式碼幾乎人人都可以看懂,我們定義了一堆變數而已,要嗎是string字串屬性的,要嗎是數值。但這裡又是什麼鬼?看起來似乎我們把方法賦給了變數?這樣做,確定系統真的不會炸?
是的,這是一種特殊的方法,因為所有的類其實都會偷偷呼叫一個初始化方法。這個初始化方法會建立指定物件的例項,這裡就是ChecklistItem。而通過這種方式,我們就可以建立了一個ChecklistItem的空白例項,其預設值就是我們定義類時所設定的~
除了這種方法,我們其實還可以用另外一種方式來定義物件:
var row0item: ChecklistItem
但如果這樣做的話,那麼row0item並不會有預設的初始值,只是建立了一個ChecklistItem物件的容器而已。在這種情況下,我們仍然需要隨後在程式碼中手動建立ChecklistItem的例項。
所以,使用 var row0item = ChecklistItem() 這種看起來頗為怪異的程式碼,我們一舉兩得。
需要再次強調的是,只是定義一個變數並不代表你就實實在在擁有了一個對應的物件。所定義的變數只是物件的容器,我們還需要初始化物件,然後將其放置到容器之中。幸運的是,在swift中只需要在類名之後加上一對括號,就幫我們搞定了後面的一半工作~
好了,這樣就行了嗎?
顯然你並不能這麼樂觀,因為你會看到Xcode提示出現大量的錯誤:x:~
Don’t panic!一切盡在掌握之中!
這是因為在檢視控制器的⼀些方法中會用到之前所定義的例項變數。現在我們⼀股腦兒全部刪除了, Xcode不瘋掉才怪。
為了讓Xcode不再冷眼相對,⾸先我們要修復這些錯誤。
1.替換tableView(_:cellForRowAt:) 方法中的程式碼如下:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "ChecklistItem", for: indexPath) //新增下面的程式碼 let label = cell.viewWithTag(1000) as! UILabel if indexPath.row== 0 { label.text = row0item.text } else if indexPath.row== 1{ label.text = row1item.text } else if indexPath.row== 2{ label.text = row2item.text } else if indexPath.row == 3{ label.text = row3item.text }else if indexPath.row== 4{ label.text = row4item.text } //結束以上的新程式碼段 configureCheckmark(for: cell, at: indexPath) return cell }
2.替換tableView(_:didSelectRowAt:) 方法的程式碼如下:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { if let cell = tableView.cellForRow(at: indexPath){ if indexPath.row == 0 { row0item.checked = !row0item.checked } else if indexPath.row == 1 { row1item.checked = !row1item.checked } else if indexPath.row == 2 { row2item.checked = !row2item.checked }else if indexPath.row == 3 { row3item.checked = !row3item.checked }else if indexPath.row == 4 { row4item.checked = !row4item.checked } configureCheckmark(for: cell, at: indexPath) } tableView.deselectRow(at: indexPath, animated: true) }
3. 最後更改configureCheckmark(for:at:)方法的程式碼如下:
func configureCheckmark(for cell: UITableViewCell, at indexPath: IndexPath){ var isChecked = false if indexPath.row == 0{ isChecked = row0item.checked }else if indexPath.row == 1{ isChecked = row1item.checked }else if indexPath.row == 2{ isChecked = row2item.checked }else if indexPath.row == 3{ isChecked = row3item.checked }else if indexPath.row == 4{ isChecked = row4item.checked } if isChecked { cell.accessoryType = .checkmark }else{ cell.accessoryType = .none } }
以上程式碼看起來⼀大堆,其實改動很少,只不過用ChecklistItem物件的屬性變數替代了之前所定義
的例項變數。 就好⽐之前你直接在這⾥定義了⼏個例項變數:我的房產估值,我的股票市值,我的現⾦,而現在⽤我的資產.房產估值,我的資產.股票價值,我的資產.現金來替代了它們。道理是⼀樣的,只是沒那麼直觀。
需要再次提醒大家的是:
如果你想學的不紮實,就還是別讓自己太勞累了,敲什麼程式碼啊,直接拷貝貼上程式碼吧。保證你看完全部課程最後啥也不剩下~
那啥賣油翁不是說過的嗎,⽆它,唯手熟爾。⽇日敲程式碼千百⾏,不會AB也會C。
好了,編譯執行一下看看是什麼情況?

:question:老夫看到的竟然是一片空白?難道是昨晚夜觀天象被神雷傷目?
當然不是,試著點選前五行。奇怪了,竟然會看到勾選標記隨著你的操作時而出現,時而消失。
這麼神奇的事情,究竟是怎麼導致的?
欲知後事如何,且聽下回分解~
聯絡方式:
- 微信討論群請先添加個人微信iseedo,說明要加入iOS開發討論
- QQ討論群:375143733
- 微信公眾號:icodefun
答疑說明:
有相關的問題請到課程答疑專區提問: http:// icode.fun/ask/forum.php