這一只是我的一個座右銘-集中和簡單.簡單,可以比實現復雜的東西更難.你必須花很多力氣去讓你的思維變得簡單,有條理.但最終它的價值非常大,因為一旦你到達了那一步,你就可以撼動山脈.
-Steve Jobs
現在你們已經對我們的demo app原型設計有了一個基本的認識,這一章我們將繼續做一些更有興趣的事然后用UITableView來構建一個簡單的基于表的app.一旦你掌握了這個技術和表格視圖定制(我們將在下一章討論),我們將開始構建Food Pin app(暫時我還不知道這是什么軟件.)
首先,在iPhone app里什么是正確的表格視圖(table view)?表格視圖(table view)在大多數iOS apps里是一個常見的UI元素.大多數apps(除了游戲),利用表格視圖來顯示內容.最好的例子是內置的電話app.你的聯系人都顯示在一個表格視圖(table view.下文都直接用table view)里.另一個例子是郵件app.它用表格視圖(table view)來顯示你的郵箱和電子郵件.table view不僅僅只為清單設計文本數據,它也允許你在圖片的形式里顯示數據.TED,Google+和Airbnb也同樣是個好例子.下圖顯示了少量基于表格的apps的例子.盡管他們看起來不一樣,但是基本上都用到了table view.
創建一個SimpleTable Project
不要光看書不練習.如果你把學習iOS編程看成一件嚴肅的事,停止閱讀.打開你的Xcode然后寫代碼!這是學習編程最好的辦法.
讓我媽開始創建一個簡單的app.這個app真的很簡單.我們僅僅在一個簡單的table view里演示一下餐廳的清單.我們將在下一章改進它.如果你沒有放棄Xcode,登陸它然后用”Single View application"模板創建一個新的工程.
點擊”Next”.為Xcode工程填寫所有要求的選項:
Product Name:SimpleTable - 這是你app的名字.
Organization Name:AppCoda - 這是你公司的名字
Organization Identifier: com.appcoda - 事實上這個域名寫反了.如果你有一個域名,你可以用你自己的域名*名字.否則,你可以用"com.appcoda"或者就填"edu.self".
Bundle Identifier: com.appcoda.StackViewDemo - 這是你app唯一的標志,用于提交app.你不需要填這個選項.Xcode會自動幫你生成.
language:Swift - 我們將用Swift來開發這個工程.
Devices: Universal - 選擇"Universal".通用app是為iPhone,iPod touch,和ipad設備優化過的單獨app.在這個demo,我們將設計一個用戶界面能運行在所有的設備上.
Use Core Data:[unchecked] - 不要選擇這個.這個簡單工程用不到Core Data.
Include Unit Tests:[unchecked] - 不要選擇這個.這個簡單工程用不到單元測試.
Include UI Tests:[unchecked] - 不要選擇這個.這個簡單工程用不到UI測試.
點擊"Next".Xcode會問你準備把StackViewDemo工程保存在哪里.在你的Mac上選擇一個文件夾,點擊"Create”.
用戶界面設計
在一個iOS app里呈現一個表格的數據,所有你需要用到的是table view對象.首先,選擇Main.storyboard來切換到界面編輯器.在對象庫里,找到Table View對象把它拖到視圖里.
選擇table view.在Attributes inspector(如果它沒有出現在你的Xcode,選擇View>Utilities>Show Attributes Inspector)里,把Prototype Cells的數字從0改成1.一個原型單元格(prototype cell)將會出現在table view里.Prototype cells允許你來簡單的設計table view單元格的布局.它同樣帶來了一些標準的單元格樣式包括basic,right detail,left detail和subtitle來讓你選擇.在這里例子里,我們只用basic樣式.關于自定義表格,我會把它留到下一章講解.
選擇單元格然后打開Attributes inspector.把cell樣式(Style)改成Basic.這個樣式用來顯示文字和圖片都有的單元格很好用.此外,把identifier設置成Cell.這是識別prototype cell獨一無二的鍵.我們將在之后的代碼里用到它.
在不使用任何代碼的情況下運行你的app
試著在模擬器里運行你的app.點擊”Run”按鈕來構建并測試你的app.模擬器的屏幕將會看起來像下面的圖一樣
很簡單對嗎?你已經為你的app創建了table view.同時,它沒有顯示任何的數據.如果你仔細看看table view,會發現它并沒有完美的拉伸到整個屏幕.如果你徹底的理解了auto layout,我相信你應該知道原因.
到現在為止我們還沒有為table view定義任何布局約束.這就是為什么它看起來有些奇怪.現在在界面編輯器里選擇table view,在布局欄里點擊Pin按鈕.設置上下左右的間距.
選擇每一個紅色虛線的標志.一旦選好了,紅色虛線會變成紅色實線.點擊”Add 4 Constraints”按鈕來添加約束.這里我們為table view的每條邊定義4個間隔約束.在這里,我們確保UITableView的地步和Bottom Layout Guide之間沒有間距.兩個水平間距約束確保table view的左右兩邊將拉伸到視圖的邊緣.換句話說,你的table view將自動調整大小來填滿整個顯示設備.
你可以再運行一次這個工程.table view現在應該支持所有的屏幕大小.界面設計好以后,我們將開始進到核心部分然后寫一些代碼插入到table data.
UITableView和Protocols
我之前提到過我們處理基礎類是由iOS SDK提供的.這些類被組織在一起叫做”frameworks”.UIKit框架就是一個最常用的框架.
它提供類來構建和管理你app的用戶界面.界面編輯器對象庫里所列的所有對象都是framework提供的.你在HelloWorld app里用到的Button對象,和我們現在用到的Table View對象都是來自UIKit framework.當我們使用工具Table View的時候,真正的類是UITableView.簡單的說,對象庫里的所有的UI組件都有對應的類.你可以在對象庫里點擊任何工具然后會在pop-over菜單顯示真正的類名.
我故意把類和方法的討論留到以后的章節.如果你不能理解類,不要擔心,把它想成一個代碼模型就好了.我會在以后的章節里給你解釋它.
現在你又了一點關于Table View和UITableView之間關系的概念.我們將寫一些代碼來插入表格數據.在工程導航里選擇ViewController.swfit來在編輯框里打開文件.在UIViewController后面添加UITableViewDataSource,UITableViewDelegate來采用協議.
當你在UIViewController之后插入代碼時,Xcode查出一個錯誤.這個紅色的感嘆號標記表明這里有個錯誤.點擊編輯器左邊小的感嘆號標記,Xcode將高亮代碼行,然后顯示一條信息告訴你錯誤細節.這條信息顯示了問題的原因,但是它不會告訴你解決方案.
所以,”Type ViewController does not conform to protocol UITableViewDataSource”是什么意思?
在Swift里,UITableViewDelegate和UITableViewDataSource是已知的接口.為了在table view里顯示數據,我們必須制定一套規則并在協議里定義.這里,ViewController類是一個采用了協議,并且執行了所有強制的方法.
這可能讓人有點暈頭轉向.這些協議是什么?為什么要用協議?
好吧,我們假設你正在開始一個新的生意,你顧了一個平面設計師來設計你公司的logo.他是一個熟練的設計師,有能力創作任何logo.但是他不能馬上開始logo設計.最少,你需要提供它一些要求比如公司的名字,顏色傾向,商業性質,這樣他才能開始創作一個logo.但是,你很忙,你把這個任務委托給你的私人助手,然后讓她聯系設計師,提供logo要求給他.
在iOS編程里,UITableView類就像平面設計師.它足夠靈活來在表格形式里顯示各種數據(如圖像,文字).你可以用他來顯示國家或者聯系人姓名清單.在我們工程里,我們將用縮略圖展示餐館的清單.
但是在UITableView能夠為你顯示數據之前,它要求有人提供一些基本的信息比如:
在table view里你想要顯示多少行?
表格數據是什么?例如,你要在第二行顯示什么?你要在第五行顯示什么?
“someone”被稱作委托對象(delegate object).在上面的類比里,私人助手就是委托對象.在iOS編程里,它同樣應用委托概念通常叫做delegation pattern.一個對象依賴于另一個對象來執行一個特殊的任務.在我們工程里,ViewController就是提供表格數據的代表.下圖闡明了UITableView,協議和委托對象之間的關系.
你怎樣告訴UITableView要顯示什么數據?UITableViewDataSource協議是關鍵.它是你的數據和table view的鏈接.協議定義了你需要添加的方法清單,這里有兩個對UITableViewDataSource來說必須的方法:
tableViwe(_:numberOfRowsInSection:)
tableView(_:cellForRowAtIndexPath:)
你需要做的是提供一個對象來實現上面的方法,讓UITableView知道顯示哪一行和每一行的數據.協議也能定義一些可選的方法,不過我們不準備在這討論.
在另一方面,UITableViewDelegate協議,處理了UITableViwe的外觀?所有的方法在協議里定義都是可選的.它們讓你管理表格行的高度,配置章節的頁眉和頁腳,重新排序單元格,等等.在這章我們不改變任何方法,我們把那些留在后面的章節.
有了一些協議的基本知識,我們繼續寫這個app的代碼.選擇ViewController.swift然后給表格數據定義一個變量.給這個變量起名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”]
在這個例子中,我們使用一個數組來儲存表格數據.在Swift聲明一個數組的語法比OC簡單多了.給這些值加上雙引號然后用逗號隔開就可以了.
在Swift里,用let聲明常量用var聲明變量.和OC相比非常簡單.一個數組里所有的項都是同類型的(如:String).感謝Swift類型推斷的特點吧,你都不需要指定數組的類型.它會自動的被Swift檢測出來.Swift能夠推斷出來restaurantName變量是一個String類型.
####給純新手介紹下數組
數組在計算機編程里是一個基本的數據結構.你可以把一個數組想象成數據元素的集合.上面代碼里的restaurantNames數組,代表著一個字符串(String)元素的集合.你可以把數組想象成這樣:

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

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

這就對了.確保他們的聯系正確鏈接,你可以再次選擇Table View.點擊多功能區域里的Connections Inspector圖標.或者,你可以右擊表格來顯示聯系.

#### 測試你的app
點擊”Run”然后在模擬器里讀取你的app.你的app現在應該會顯示一個餐館清單.

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

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

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