這一只是我的一個(gè)座右銘-集中和簡單.簡單,可以比實(shí)現(xiàn)復(fù)雜的東西更難.你必須花很多力氣去讓你的思維變得簡單,有條理.但最終它的價(jià)值非常大,因?yàn)橐坏┠愕竭_(dá)了那一步,你就可以撼動(dòng)山脈.
-Steve Jobs
現(xiàn)在你們已經(jīng)對(duì)我們的demo app原型設(shè)計(jì)有了一個(gè)基本的認(rèn)識(shí),這一章我們將繼續(xù)做一些更有興趣的事然后用UITableView來構(gòu)建一個(gè)簡單的基于表的app.一旦你掌握了這個(gè)技術(shù)和表格視圖定制(我們將在下一章討論),我們將開始構(gòu)建Food Pin app(暫時(shí)我還不知道這是什么軟件.)
首先,在iPhone app里什么是正確的表格視圖(table view)?表格視圖(table view)在大多數(shù)iOS apps里是一個(gè)常見的UI元素.大多數(shù)apps(除了游戲),利用表格視圖來顯示內(nèi)容.最好的例子是內(nèi)置的電話app.你的聯(lián)系人都顯示在一個(gè)表格視圖(table view.下文都直接用table view)里.另一個(gè)例子是郵件app.它用表格視圖(table view)來顯示你的郵箱和電子郵件.table view不僅僅只為清單設(shè)計(jì)文本數(shù)據(jù),它也允許你在圖片的形式里顯示數(shù)據(jù).TED,Google+和Airbnb也同樣是個(gè)好例子.下圖顯示了少量基于表格的apps的例子.盡管他們看起來不一樣,但是基本上都用到了table view.

創(chuàng)建一個(gè)SimpleTable Project
不要光看書不練習(xí).如果你把學(xué)習(xí)iOS編程看成一件嚴(yán)肅的事,停止閱讀.打開你的Xcode然后寫代碼!這是學(xué)習(xí)編程最好的辦法.
讓我媽開始創(chuàng)建一個(gè)簡單的app.這個(gè)app真的很簡單.我們僅僅在一個(gè)簡單的table view里演示一下餐廳的清單.我們將在下一章改進(jìn)它.如果你沒有放棄Xcode,登陸它然后用”Single View application"模板創(chuàng)建一個(gè)新的工程.

點(diǎn)擊”Next”.為Xcode工程填寫所有要求的選項(xiàng):
Product Name:SimpleTable - 這是你app的名字.
Organization Name:AppCoda - 這是你公司的名字
Organization Identifier: com.appcoda - 事實(shí)上這個(gè)域名寫反了.如果你有一個(gè)域名,你可以用你自己的域名*名字.否則,你可以用"com.appcoda"或者就填"edu.self".
Bundle Identifier: com.appcoda.StackViewDemo - 這是你app唯一的標(biāo)志,用于提交app.你不需要填這個(gè)選項(xiàng).Xcode會(huì)自動(dòng)幫你生成.
language:Swift - 我們將用Swift來開發(fā)這個(gè)工程.
Devices: Universal - 選擇"Universal".通用app是為iPhone,iPod touch,和ipad設(shè)備優(yōu)化過的單獨(dú)app.在這個(gè)demo,我們將設(shè)計(jì)一個(gè)用戶界面能運(yùn)行在所有的設(shè)備上.
Use Core Data:[unchecked] - 不要選擇這個(gè).這個(gè)簡單工程用不到Core Data.
Include Unit Tests:[unchecked] - 不要選擇這個(gè).這個(gè)簡單工程用不到單元測(cè)試.
Include UI Tests:[unchecked] - 不要選擇這個(gè).這個(gè)簡單工程用不到UI測(cè)試.
點(diǎn)擊"Next".Xcode會(huì)問你準(zhǔn)備把StackViewDemo工程保存在哪里.在你的Mac上選擇一個(gè)文件夾,點(diǎn)擊"Create”.
用戶界面設(shè)計(jì)
在一個(gè)iOS app里呈現(xiàn)一個(gè)表格的數(shù)據(jù),所有你需要用到的是table view對(duì)象.首先,選擇Main.storyboard來切換到界面編輯器.在對(duì)象庫里,找到Table View對(duì)象把它拖到視圖里.

選擇table view.在Attributes inspector(如果它沒有出現(xiàn)在你的Xcode,選擇View>Utilities>Show Attributes Inspector)里,把Prototype Cells的數(shù)字從0改成1.一個(gè)原型單元格(prototype cell)將會(huì)出現(xiàn)在table view里.Prototype cells允許你來簡單的設(shè)計(jì)table view單元格的布局.它同樣帶來了一些標(biāo)準(zhǔn)的單元格樣式包括basic,right detail,left detail和subtitle來讓你選擇.在這里例子里,我們只用basic樣式.關(guān)于自定義表格,我會(huì)把它留到下一章講解.
選擇單元格然后打開Attributes inspector.把cell樣式(Style)改成Basic.這個(gè)樣式用來顯示文字和圖片都有的單元格很好用.此外,把identifier設(shè)置成Cell.這是識(shí)別prototype cell獨(dú)一無二的鍵.我們將在之后的代碼里用到它.

在不使用任何代碼的情況下運(yùn)行你的app
試著在模擬器里運(yùn)行你的app.點(diǎn)擊”Run”按鈕來構(gòu)建并測(cè)試你的app.模擬器的屏幕將會(huì)看起來像下面的圖一樣

很簡單對(duì)嗎?你已經(jīng)為你的app創(chuàng)建了table view.同時(shí),它沒有顯示任何的數(shù)據(jù).如果你仔細(xì)看看table view,會(huì)發(fā)現(xiàn)它并沒有完美的拉伸到整個(gè)屏幕.如果你徹底的理解了auto layout,我相信你應(yīng)該知道原因.
到現(xiàn)在為止我們還沒有為table view定義任何布局約束.這就是為什么它看起來有些奇怪.現(xiàn)在在界面編輯器里選擇table view,在布局欄里點(diǎn)擊Pin按鈕.設(shè)置上下左右的間距.

選擇每一個(gè)紅色虛線的標(biāo)志.一旦選好了,紅色虛線會(huì)變成紅色實(shí)線.點(diǎn)擊”Add 4 Constraints”按鈕來添加約束.這里我們?yōu)閠able view的每條邊定義4個(gè)間隔約束.在這里,我們確保UITableView的地步和Bottom Layout Guide之間沒有間距.兩個(gè)水平間距約束確保table view的左右兩邊將拉伸到視圖的邊緣.換句話說,你的table view將自動(dòng)調(diào)整大小來填滿整個(gè)顯示設(shè)備.
你可以再運(yùn)行一次這個(gè)工程.table view現(xiàn)在應(yīng)該支持所有的屏幕大小.界面設(shè)計(jì)好以后,我們將開始進(jìn)到核心部分然后寫一些代碼插入到table data.
UITableView和Protocols
我之前提到過我們處理基礎(chǔ)類是由iOS SDK提供的.這些類被組織在一起叫做”frameworks”.UIKit框架就是一個(gè)最常用的框架.
它提供類來構(gòu)建和管理你app的用戶界面.界面編輯器對(duì)象庫里所列的所有對(duì)象都是framework提供的.你在HelloWorld app里用到的Button對(duì)象,和我們現(xiàn)在用到的Table View對(duì)象都是來自UIKit framework.當(dāng)我們使用工具Table View的時(shí)候,真正的類是UITableView.簡單的說,對(duì)象庫里的所有的UI組件都有對(duì)應(yīng)的類.你可以在對(duì)象庫里點(diǎn)擊任何工具然后會(huì)在pop-over菜單顯示真正的類名.

我故意把類和方法的討論留到以后的章節(jié).如果你不能理解類,不要擔(dān)心,把它想成一個(gè)代碼模型就好了.我會(huì)在以后的章節(jié)里給你解釋它.
現(xiàn)在你又了一點(diǎn)關(guān)于Table View和UITableView之間關(guān)系的概念.我們將寫一些代碼來插入表格數(shù)據(jù).在工程導(dǎo)航里選擇ViewController.swfit來在編輯框里打開文件.在UIViewController后面添加UITableViewDataSource,UITableViewDelegate來采用協(xié)議.
當(dāng)你在UIViewController之后插入代碼時(shí),Xcode查出一個(gè)錯(cuò)誤.這個(gè)紅色的感嘆號(hào)標(biāo)記表明這里有個(gè)錯(cuò)誤.點(diǎn)擊編輯器左邊小的感嘆號(hào)標(biāo)記,Xcode將高亮代碼行,然后顯示一條信息告訴你錯(cuò)誤細(xì)節(jié).這條信息顯示了問題的原因,但是它不會(huì)告訴你解決方案.

所以,”Type ViewController does not conform to protocol UITableViewDataSource”是什么意思?
在Swift里,UITableViewDelegate和UITableViewDataSource是已知的接口.為了在table view里顯示數(shù)據(jù),我們必須制定一套規(guī)則并在協(xié)議里定義.這里,ViewController類是一個(gè)采用了協(xié)議,并且執(zhí)行了所有強(qiáng)制的方法.
這可能讓人有點(diǎn)暈頭轉(zhuǎn)向.這些協(xié)議是什么?為什么要用協(xié)議?
好吧,我們假設(shè)你正在開始一個(gè)新的生意,你顧了一個(gè)平面設(shè)計(jì)師來設(shè)計(jì)你公司的logo.他是一個(gè)熟練的設(shè)計(jì)師,有能力創(chuàng)作任何logo.但是他不能馬上開始logo設(shè)計(jì).最少,你需要提供它一些要求比如公司的名字,顏色傾向,商業(yè)性質(zhì),這樣他才能開始創(chuàng)作一個(gè)logo.但是,你很忙,你把這個(gè)任務(wù)委托給你的私人助手,然后讓她聯(lián)系設(shè)計(jì)師,提供logo要求給他.
在iOS編程里,UITableView類就像平面設(shè)計(jì)師.它足夠靈活來在表格形式里顯示各種數(shù)據(jù)(如圖像,文字).你可以用他來顯示國家或者聯(lián)系人姓名清單.在我們工程里,我們將用縮略圖展示餐館的清單.
但是在UITableView能夠?yàn)槟泔@示數(shù)據(jù)之前,它要求有人提供一些基本的信息比如:
在table view里你想要顯示多少行?
表格數(shù)據(jù)是什么?例如,你要在第二行顯示什么?你要在第五行顯示什么?
“someone”被稱作委托對(duì)象(delegate object).在上面的類比里,私人助手就是委托對(duì)象.在iOS編程里,它同樣應(yīng)用委托概念通常叫做delegation pattern.一個(gè)對(duì)象依賴于另一個(gè)對(duì)象來執(zhí)行一個(gè)特殊的任務(wù).在我們工程里,ViewController就是提供表格數(shù)據(jù)的代表.下圖闡明了UITableView,協(xié)議和委托對(duì)象之間的關(guān)系.

你怎樣告訴UITableView要顯示什么數(shù)據(jù)?UITableViewDataSource協(xié)議是關(guān)鍵.它是你的數(shù)據(jù)和table view的鏈接.協(xié)議定義了你需要添加的方法清單,這里有兩個(gè)對(duì)UITableViewDataSource來說必須的方法:
tableViwe(_:numberOfRowsInSection:)
tableView(_:cellForRowAtIndexPath:)
你需要做的是提供一個(gè)對(duì)象來實(shí)現(xiàn)上面的方法,讓UITableView知道顯示哪一行和每一行的數(shù)據(jù).協(xié)議也能定義一些可選的方法,不過我們不準(zhǔn)備在這討論.
在另一方面,UITableViewDelegate協(xié)議,處理了UITableViwe的外觀?所有的方法在協(xié)議里定義都是可選的.它們讓你管理表格行的高度,配置章節(jié)的頁眉和頁腳,重新排序單元格,等等.在這章我們不改變?nèi)魏畏椒?我們把那些留在后面的章節(jié).
有了一些協(xié)議的基本知識(shí),我們繼續(xù)寫這個(gè)app的代碼.選擇ViewController.swift然后給表格數(shù)據(jù)定義一個(gè)變量.給這個(gè)變量起名restaurantNames然后插入下面的代碼:
var restaurantNames = ["Cafe Deadend", "Homei", "Teakha", "Cafe Loisl", "Petite Oyster", "For Kee Restaurant", "Po's Atelier", "Bourke Street Bakery", "Haigh's Chocolate", "Palomino Espresso", "Upstate", "Traif", "Graham Avenue Meats And Deli", "Waffle & Wolf", "Five Leaves", "Cafe Lore", "Confessional", "Barrafina", "Donostia", "Royal Oak", "CASK Pub and Kitchen”]
在這個(gè)例子中,我們使用一個(gè)數(shù)組來儲(chǔ)存表格數(shù)據(jù).在Swift聲明一個(gè)數(shù)組的語法比OC簡單多了.給這些值加上雙引號(hào)然后用逗號(hào)隔開就可以了.
在Swift里,用let聲明常量用var聲明變量.和OC相比非常簡單.一個(gè)數(shù)組里所有的項(xiàng)都是同類型的(如:String).感謝Swift類型推斷的特點(diǎn)吧,你都不需要指定數(shù)組的類型.它會(huì)自動(dòng)的被Swift檢測(cè)出來.Swift能夠推斷出來restaurantName變量是一個(gè)String類型.
####給純新手介紹下數(shù)組
數(shù)組在計(jì)算機(jī)編程里是一個(gè)基本的數(shù)據(jù)結(jié)構(gòu).你可以把一個(gè)數(shù)組想象成數(shù)據(jù)元素的集合.上面代碼里的restaurantNames數(shù)組,代表著一個(gè)字符串(String)元素的集合.你可以把數(shù)組想象成這樣:

每一個(gè)數(shù)組元素都可以由索引確定或者訪問.有10個(gè)元素的數(shù)組將有0到9的索引.這意味著restaurantNames[0]返回的是數(shù)組的第一項(xiàng).
我們繼續(xù)碼字.我們給UITableViewDataSource協(xié)議添加兩個(gè)必須的方法.
> func tableView(tableView: UITableView, numberofRowsInSection section: Int) ->INT
{
// 在章節(jié)里返回行數(shù)
return restaurantNames.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = “Cell”
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)
// 確認(rèn)單元格...
cell.textLabel?.text = restaurantNames[indexPath.row]
return cell
}
第一個(gè)方法用來制定table view里一個(gè)章節(jié)的總行數(shù)(table view 可以有多個(gè)部分但是只有一個(gè)默認(rèn)的.)你可以簡單的調(diào)用count方法來得到restaurantNames數(shù)組里物品的數(shù)字.
第二個(gè)方法叫做每次顯示一行.使用indexPath對(duì)象,我們能得到當(dāng)下的行數(shù)(indexPath.row).這里我們做的是從restaurantNames數(shù)組里檢索索引項(xiàng),然后把它分配給文本標(biāo)簽(cell.textLabel.text)來顯示.
okay,但是第二行代碼的dequeueReusableCellWithIdentifier是什么?
這個(gè)dequeueReusableCellWithIdentifier方法用來從一個(gè)有著特殊單元格標(biāo)識(shí)符的隊(duì)列里檢索一個(gè)可重復(fù)使用的單元格.這個(gè)單元格標(biāo)識(shí)符是我們之前在界面編輯器里定義的.
你想要你基于表格的app反應(yīng)快,即使處理數(shù)千行數(shù)據(jù)也有求必應(yīng).如果你給你一行數(shù)據(jù)分配一個(gè)新的單元格而不是重復(fù)利用一個(gè),你的app將使用過多的內(nèi)存,并可能導(dǎo)致當(dāng)用戶滾動(dòng)table view時(shí)感覺很遲鈍.記住每一次單元格的分配都是一次性能消耗.尤其是當(dāng)分配發(fā)生在一個(gè)很短的周期之類.
iPhone屏幕的真實(shí)空間是有限的.即使你的app需要顯示1000條記錄,屏幕可能最多只能填10個(gè)單元格.因此,究竟為什么要分配上千個(gè)圖標(biāo)單元格而不是創(chuàng)建10個(gè)單元格然后重復(fù)利用它們呢?這將節(jié)約成噸的內(nèi)存然后讓表格視圖更有效率的工作.考慮到性能的原因,你應(yīng)該重復(fù)利用表格視圖單元格來替換創(chuàng)建新的單元格.

現(xiàn)在,我們來點(diǎn)擊”Run”按鈕來測(cè)試你的app.哎喲!這個(gè)app依然跟以前一樣顯示一個(gè)空白的表格視圖.
為什么table view不像我們希望的那樣顯示內(nèi)容呢?我們已經(jīng)為顯示表格數(shù)據(jù)寫了代碼然后實(shí)施了所有必須的要求.但是為什么table view不像我們希望的來顯示內(nèi)容呢?
有一件事被遺忘了.
#### 連接DataSource和Delegate
盡管ViewController類已經(jīng)添加到UITableViewDelegate和UITableViewDataSource協(xié)議里,但是storyboard里的UITableView對(duì)象不知道它.我們應(yīng)該告訴UITableView對(duì)象ViewController是數(shù)據(jù)源的委托對(duì)象.
回到Main.storyboard.選擇table view.按住control鍵,拖曳table view到文件大綱里的View Controller對(duì)象.

這就對(duì)了.確保他們的聯(lián)系正確鏈接,你可以再次選擇Table View.點(diǎn)擊多功能區(qū)域里的Connections Inspector圖標(biāo).或者,你可以右擊表格來顯示聯(lián)系.

#### 測(cè)試你的app
點(diǎn)擊”Run”然后在模擬器里讀取你的app.你的app現(xiàn)在應(yīng)該會(huì)顯示一個(gè)餐館清單.

####為Table View添加縮略圖
給每一行添加一個(gè)圖片是不是很好呢?UITableView做這個(gè)很簡單.你僅僅需要添加一行代碼來給每一行插入縮略圖.
從Finder拖動(dòng)圖片到Assets.xcassets

現(xiàn)在編輯ViewController.Swift然后添加下面這行代碼到tableView(_:cellForRowAtIndexPath:)方法.把它放在return cell:cell.imageView?.image = UIImage(named: “restaurant”)之前.
UIKit框架提供的UIImage類讓你從文件里創(chuàng)作圖片.它支持各種圖片格式例如PNG,GIF和JPEG.去掉圖片的名字(文件擴(kuò)展是可選的)然后這個(gè)類會(huì)讀取圖片.
再一次調(diào)取我們用過的Basic單元格格式,它帶來一個(gè)默認(rèn)的區(qū)域來展示圖片或者縮略圖.這條代碼介紹UITableView來讀取圖片然后在單元格的圖片視圖里顯示它.現(xiàn)在,點(diǎn)擊”Run”按鈕你的SimpleTable app應(yīng)該會(huì)在每一行顯示圖片.

#### 隱藏狀態(tài)欄
table view的內(nèi)容與狀態(tài)欄重疊了.這看起來不太好.一個(gè)簡單補(bǔ)救辦法是隱藏狀態(tài)欄.你可以在每個(gè)視圖控制器的基礎(chǔ)上控制狀態(tài)欄的顯示.如果你不想在一個(gè)特殊的視圖控制器顯示狀態(tài)欄,簡單的添加下面的代碼行:
> override func prefersStatusBarHidden() -> Bool {
return true
}
插入這行代碼到ViewController.swift然后測(cè)試app.你可以看到一個(gè)沒有狀態(tài)欄的全屏表格視圖.