這本書(shū)看了幾遍也記不清了转绷,總之好多遍,第一遍照著書(shū)中的代碼敲了一遍硼啤,結(jié)果還出現(xiàn)了好多bug议经,刪掉重新敲,沒(méi)有問(wèn)題之后谴返,又看了一遍煞肾,理解一下大體意思,再看的時(shí)候稍微自己設(shè)計(jì)了一個(gè)相似的應(yīng)用嗓袱,然后利用學(xué)到的知識(shí)開(kāi)發(fā)出來(lái)籍救,功能運(yùn)行沒(méi)有問(wèn)題,就是界面太丑了索抓。
iOS Apprentice系列一共有四本钧忽,這是第二本。目前看的這版是第四版逼肯,V4.1,升級(jí)到了iOS 9桃煎、Xcode7和Swif2.0篮幢,適合完全沒(méi)有編程基礎(chǔ)的人和初學(xué)者來(lái)閱讀。這本書(shū)的可以在RayWenderlich官網(wǎng)注冊(cè)后訂閱Newsletter免費(fèi)獲得:獲取地址为迈。需要花錢購(gòu)買三椿,價(jià)格不菲,美國(guó)本來(lái)書(shū)就貴葫辐,美元匯率的原因?qū)е码S筆買本書(shū)就三四百RMB出去了搜锰,肉疼。
這本書(shū)比起第一本來(lái)說(shuō)耿战,難度上立馬上了一個(gè)層次蛋叼,不多看幾遍,還真有時(shí)候會(huì)跟不上節(jié)奏。我總感覺(jué)需要看上十遍八遍的方能放心狈涮。
好了狐胎,廢話不多說(shuō),下面就開(kāi)始總結(jié)一下書(shū)中的重點(diǎn)知識(shí)歌馍。
1. Upside down
在設(shè)計(jì)App的時(shí)候握巢,盡量不使用這個(gè)方向。如果你的App支持這個(gè)方向松却,Home按鈕可以出現(xiàn)在上方暴浦,當(dāng)用戶接到電話時(shí)會(huì)帶來(lái)不便,麥克風(fēng)會(huì)在上方離職嘴巴比較遠(yuǎn)晓锻,而不是下面靠近嘴巴的地方歌焦。
不過(guò)對(duì)于iPad應(yīng)用來(lái)說(shuō),最好是能夠支持全部的四個(gè)方向带射,畢竟人們?cè)谑褂胕Pad時(shí)同规,一般沒(méi)有接電話的需求。(不過(guò)類似facetime微信視頻之類App在iPad可能需要注意一下麥克風(fēng)了)
2.UItableView object
這個(gè)控件用來(lái)展示清單列表窟社。有兩種風(fēng)格: “plain” 和 “grouped”券勺,請(qǐng)見(jiàn)下圖(左邊是plain風(fēng)格,右邊是grouped風(fēng)格):
數(shù)據(jù)是以行的形式展示灿里,一行(row)就是一條數(shù)據(jù)关炼。
你可以有成千上萬(wàn)行的數(shù)據(jù),盡管這種設(shè)計(jì)模式我們不推薦匣吊,大部分用戶會(huì)討厭一直往下滑成千上萬(wàn)行才能找到他們想要看到的內(nèi)容儒拂。
UItableView控件使用cell來(lái)展示數(shù)據(jù)。一個(gè)cell對(duì)應(yīng)一個(gè)row色鸳,但是cell和row不完全相同社痛。首先cell是一個(gè)view,cell的數(shù)量命雀,是由在某一刻蒜哀,可以看到的row(行)的數(shù)量決定的。加入在iPhone屏幕上一次最多能展示10rows吏砂,想看到更多row就需要往上滑動(dòng)屏幕撵儿,這時(shí)候,這里只有10個(gè)cell狐血,盡管實(shí)際上可能會(huì)有成千行(row)數(shù)據(jù)淀歇。
當(dāng)一行數(shù)據(jù)被往上移動(dòng)移出屏幕不可見(jiàn)后,cell會(huì)被重復(fù)利用匈织,接著用來(lái)展示新出現(xiàn)在屏幕中的那些行數(shù)據(jù)浪默。下圖展示了row和cell的區(qū)別:
在過(guò)去,你需要寫(xiě)好多代碼才能復(fù)用cell,但是現(xiàn)在Xcode給出了一個(gè)非常有用的特性名為prototype cells浴鸿,可以讓你在界面上直接設(shè)計(jì)cell井氢,然后復(fù)用cell。
創(chuàng)建cell的最簡(jiǎn)單的方法:
1.在storyboard中添加一個(gè)prototype cell岳链;
2.在prototype cell上設(shè)置它的reuse identifier花竞;
3.調(diào)用tableView.dequeueReusableCellWithIdentifier(forIndexPath)
方法
3. The data source(數(shù)據(jù)來(lái)源)
使用UITableView首先需要你把storyboard中的UITableView和對(duì)應(yīng)的.swift文件建立關(guān)聯(lián),同時(shí)也需要遵守data source protocol掸哑。
UITableView和對(duì)應(yīng)的.swift文件建立關(guān)聯(lián)最快的方法:
在storyboard中選中UITableView约急,同時(shí)按住Control鍵,鼠標(biāo)拖向紅點(diǎn)(見(jiàn)下圖)
當(dāng)然了苗分,如果你在創(chuàng)建.swift文件的時(shí)候厌蔽,SubClass選擇了UITableViewController,Xcode會(huì)自動(dòng)幫你建立關(guān)聯(lián)(delegate和data source都自動(dòng)建立)摔癣。如果你選擇的是普通的UIViewController奴饮,那么就需要使用上圖中的方法手動(dòng)建立。
除了鼠標(biāo)拖動(dòng)择浊,還有代碼方法:
delegate = self
遵守data source protocol
協(xié)議中有2個(gè)方法是必須有的戴卜,其他的方法可選。
下列代碼中的2個(gè)方法是必須要有的:
override func tableView(tableView: UITableView,numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView,cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ChecklistItem", forIndexPath: indexPath)
return cell
}
至于為什么必須要有這兩個(gè)方法琢岩,看下圖投剥,下圖中UITableViewDataSource是一個(gè)協(xié)議protocol,我用紅色線框和黃色線框標(biāo)注出來(lái)了担孔,紅色是public func江锨,黃色是optional public func。
綜上所述糕篇,分為3步吧:
- 表示當(dāng)前的這個(gè)view controller(一般就是.swift文件)是你(UITableView)的data source(數(shù)據(jù)來(lái)源)啄育,當(dāng)我們使用
delegate = self
時(shí),我們就把view controller(視圖控制器,以.swift結(jié)尾的文件)和UITableView連接起來(lái)了拌消。 - 當(dāng)table view需要知道自己有多少行時(shí)灸撰,就會(huì)發(fā)給controller一個(gè)“numberOfRowsInSection”的message,來(lái)獲取當(dāng)前table view一共有多少行拼坎。
- 當(dāng)table view需要知道自己某一行呈現(xiàn)哪些內(nèi)容時(shí),就會(huì)發(fā)給controller一個(gè)“cellForRowAtIndexPath” 的message完疫,來(lái)獲取這當(dāng)前行cell的數(shù)據(jù)泰鸡。
你會(huì)在iOS中經(jīng)常看到這種模式:一個(gè)對(duì)象代表另外一個(gè)對(duì)象做一些事情壳鹤。在這里的例子中盛龄,ChecklistViewController是給table view提供數(shù)據(jù),不過(guò)只有在table view請(qǐng)求的時(shí)候才會(huì)提供。
4. Return語(yǔ)法
在上面的代碼中余舶,numberOfRowsInSection方法中返回了數(shù)值1啊鸭,表示當(dāng)前table view只有一行數(shù)據(jù)。
return語(yǔ)法在swift中非常重要匿值,return允許一個(gè)方法能夠?qū)?shù)據(jù)返回給方法的調(diào)用者(caller)赠制。在這里的例子中,調(diào)用者(caller)就是UITableView對(duì)象挟憔,UITableView對(duì)象想知道table里有多少行钟些。
返回的數(shù)值經(jīng)常被叫做是方法的結(jié)果。
5. 調(diào)用次數(shù)(重點(diǎn)绊谭!重點(diǎn)U小!重點(diǎn)4锎8莺摹!)
當(dāng)我們使用tableView(numberOfRowsInSection)
方法時(shí)宪赶,如果返回值是5(return 5),那么實(shí)際上你是在讓table view顯示5行宗弯。
同時(shí),table view會(huì)把“cellForRowAtIndexPat”消息發(fā)送5次逊朽,一行一次罕伯。
如果不理解這一點(diǎn),在后面使用
tableView(cellForRowAtIndexPath)
方法時(shí)叽讳,容易出錯(cuò)或者不知所措~
6. Index paths講解
什么是index path?
首先NSIndexPath是一個(gè)對(duì)象(object)追他,這個(gè)對(duì)象用來(lái)指出table中某個(gè)具體的行(row),NSIndexPath里面包含了row number(行數(shù))和section number(區(qū)域數(shù))岛蚤,僅此兩個(gè)邑狸。當(dāng)tableview需要獲取某個(gè)cell的數(shù)據(jù)data source時(shí),你可以通過(guò)indexPath.row
屬性查找row number涤妒,然后找出當(dāng)前cell到底想獲取哪行row的數(shù)據(jù)单雾。
table也可以將rows行分布到不同的section中,例如在手機(jī)通訊錄中她紫,可以通過(guò)姓氏來(lái)排列組合硅堆,所有的同一姓氏聯(lián)系人為一個(gè)section。
想要找出某row屬于哪個(gè)section贿讹,可以使用indexPath.section
屬性渐逃。
順便說(shuō)一下,NSIndexPath的前綴NS是NextStep的縮寫(xiě)民褂,帶有NS前綴的object都是由Foundation框架提供的茄菊。NextStep是一個(gè)操作系統(tǒng)疯潭,是Mac OS X 和iOS的前身。
7. Tag
Tag就相當(dāng)于是用數(shù)字做的標(biāo)識(shí)符(identifier)面殖,我們之前使用的identifier都是自己輸入的字符串竖哩,Tag相當(dāng)于是用數(shù)字做identifier,這樣使controller(.swift文件)能夠知道自己控制的是storyboard上哪個(gè)控件脊僚。Tag的默認(rèn)值是0相叁,所以當(dāng)你使用Tag時(shí),數(shù)字要大于0吃挑,
為什么在用cell時(shí)要用tag呢钝荡?我們之前學(xué)會(huì)的control拖動(dòng)法建立Outlet連接在這里為什么不能用?
回答:有時(shí)候table上有很多個(gè)cell舶衬,每個(gè)cell都有自己的label等控件埠通,如果我們使用Control拖動(dòng)法建立outlet連接,這個(gè)outlet只是表示當(dāng)前cell的label逛犹,而table上有很多個(gè)cell端辱。而且label控件是輸入cell的,不是輸入View controller虽画,所以不能使用outlet舞蔽。
我覺(jué)得這里有必要附上英文原文,以防我理解有誤码撰,引起誤會(huì):
Answer: There will be more than one cell in the table and each cell will have its ownlabel. If you connected the label from the prototype cell to an outlet on the view controller, that outlet could only refer to the label from one of these cells, not all of them. Since the label belongs to the cell and not to the view controller as a whole,you can’t make an outlet for it on the view controller. Confused? Don’t worry aboutif for now.
8. 小知識(shí)點(diǎn)
- 從零開(kāi)始渗柿,不是從一開(kāi)始。
計(jì)算機(jī)計(jì)算都是從零開(kāi)始的脖岛,第一個(gè)數(shù)值是0朵栖,第二個(gè)數(shù)值是2,在Array類型中最常見(jiàn)柴梆。如果數(shù)組中有4個(gè)數(shù)字陨溅,那么序列就是0,1,2,3。一開(kāi)始可能不太習(xí)慣绍在,畢竟我們平時(shí)都是從一開(kāi)始數(shù)數(shù)的门扇,不過(guò)這就是電腦數(shù)數(shù)的方式,習(xí)慣就好偿渡。 - 取模運(yùn)算臼寄,求余數(shù),符號(hào)是%溜宽。
9. 奇怪的崩潰
如果你的程序奇怪的崩潰了脯厨,首先你要確認(rèn)一下你是否不小心在代碼中設(shè)置了一個(gè)斷點(diǎn)(breakpoint)。斷點(diǎn)是一種調(diào)試代碼的工具坑质,能讓你的程序在執(zhí)行到某行代碼時(shí)停止編譯合武,同時(shí)顯示Xcode的debugger窗口,看起來(lái)像是程序崩潰涡扼,其實(shí)只是程序停止執(zhí)行稼跳。
斷點(diǎn)就是下圖中左邊邊界上的藍(lán)色箭頭,箭頭方向朝右:
10. 用戶體驗(yàn)的小細(xì)節(jié)(細(xì)節(jié)決定成敵曰Α)
當(dāng)你點(diǎn)擊某行cell后汤善,這行會(huì)顯示灰色。然后灰色就會(huì)一直存在票彪,直到你點(diǎn)擊另外一行红淡,另外一行變灰色。如果想在點(diǎn)擊時(shí)灰色降铸,手松開(kāi)后灰色就消失不見(jiàn)在旱,可以使用table view中的delegate中的方案來(lái)實(shí)現(xiàn)。
我們之前了解到推掸,cell的顯示內(nèi)容也就是數(shù)據(jù)由data source控制桶蝎,那么用戶點(diǎn)擊cell或者和cell的其他手勢(shì)交互則由delegate控制。
11. 委托模式 The delegation pattern
在iOS中谅畅,delegation的概念非常常見(jiàn)登渣。一個(gè)object常常借助另外一個(gè)object來(lái)實(shí)現(xiàn)某個(gè)任務(wù),這樣做的好處很多毡泻,因?yàn)槊總€(gè)object只需要做自己最擅長(zhǎng)的事情胜茧,然后讓其他的object做它們更擅長(zhǎng)的事情,這種理念在table view中得到了很好的提現(xiàn)仇味。
因?yàn)椴煌腁pp對(duì)于自己的數(shù)據(jù)有不同的要求呻顽,所有table view必須具有處理各種不同類型數(shù)據(jù)的能力。為了不讓table view過(guò)于復(fù)雜邪铲,UIKit的設(shè)計(jì)者選擇委托其他對(duì)象(也就是data source)來(lái)完成填充cell顯示內(nèi)容這項(xiàng)任務(wù)芬位。
table view不關(guān)心誰(shuí)是它的data source,也不關(guān)心目前處理的數(shù)據(jù)是何種類型带到,只要這個(gè)data source能夠把消息發(fā)送給cellForRowAtIndexPath昧碉,table view收到一個(gè)cell作為返回值,就行了揽惹。這種模式能夠讓table view更輕盈被饿,table view把處理數(shù)據(jù)的責(zé)任轉(zhuǎn)給了:你的代碼。
同樣的搪搏,table view也能識(shí)別出用戶點(diǎn)擊了某行row狭握,但是之后要做出哪些事情來(lái)回應(yīng)用戶的點(diǎn)擊行為呢?在我們這本書(shū)checklist應(yīng)用中疯溺,點(diǎn)擊之后是出現(xiàn)一個(gè)對(duì)勾论颅,但是其他的App會(huì)有不同的反應(yīng)哎垦。
使用委托delegate,table view需要做的只是發(fā)送消息而已恃疯,比如消息是:一個(gè)點(diǎn)擊事件發(fā)生了漏设,然后讓delegate處理之后的事情。
override func tableView(tableView: UITableView,didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
}
通常情況下只需要一個(gè)delegate就可以了今妄,但是table view有兩個(gè)delegate:UITableViewDataSource處理數(shù)據(jù)郑口,UITableViewDelegate處理點(diǎn)擊事情和其他事件。
12. tableView.cellForRowAtIndexPath()
和tableView(cellForRowAtIndexPath)
區(qū)別
override func tableView(tableView: UITableView,didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
if cell.accessoryType == .None {
cell.accessoryType = .Checkmark
} else {
cell.accessoryType = .None
}
}
tableView.deselectRowAtIndexPath(indexPath, animated: true)}
獲取被點(diǎn)擊的Row的NSIndexPath
為了找到這行cell盾鳞,你需要調(diào)用tableView.cellForRowAtIndexPath()
方法犬性,你需要意識(shí)到,這和之前在data source中方法tableView(cellForRowAtIndexPath)
不是一回事腾仅,這點(diǎn)很重要乒裆。盡管從方法名字上看起來(lái)挺像的,但是這兩個(gè)方法是不同對(duì)象中的不同的方法攒砖,達(dá)成不同的任務(wù)缸兔。這看起來(lái)很容易讓人混淆,對(duì)吧吹艇?
data source中tableView(cellForRowAtIndexPath)
的方法是創(chuàng)建或者復(fù)用cell惰蜜,你永遠(yuǎn)不會(huì)調(diào)用這個(gè)方法,只有UITableView可以調(diào)用data source方法受神。
而tableView.cellForRowAtIndexPath()
返回的是cell對(duì)象抛猖,但是這個(gè)cell是當(dāng)前被展示在屏幕上的某一行(row)所在的cell。tableView.cellForRowAtIndexPath()
不會(huì)創(chuàng)建新的cell鼻听。如果當(dāng)前界面上沒(méi)有所需的cell财著,就會(huì)返回一個(gè)nil值。
記得我在之前的章節(jié)中說(shuō)過(guò)撑碴,方法的命名要簡(jiǎn)潔明確有描述作用嗎撑教?UIKit在這方面做的一直不錯(cuò),但是這兩個(gè)方法是一個(gè)特例醉拓,兩個(gè)相同的名字用在了不同的地方伟姐,這樣會(huì)給開(kāi)發(fā)者帶來(lái)困惑和麻煩。要小心這個(gè)坑 亿卤,別跳進(jìn)去了愤兵!
13. UITableViewController的優(yōu)勢(shì)
在這圖中可以看到,data Source和delegate都已經(jīng)連接到對(duì)應(yīng)的view controller中了排吴,這是 UITableViewController的標(biāo)準(zhǔn)設(shè)置秆乳,如果你使用的是 UIViewController,就需要手作關(guān)聯(lián)data source和delegate了。