在本課程中,您將建立應用程序FoodTracker的主屏幕。您將建立第二個,表視圖爲主場景,列出了用戶的菜譜。你會設計定製表格單元格顯示每個菜譜,它是這樣的:html
學習目標android
在課程結束時,你將可以:ios
建立第二個storyboard 場景
瞭解table view這個關鍵組件
建立和設計自定義table view單元格
瞭解table view委託和數據源
使用數組來存儲和處理數據
在table view中顯示動態數據swift
建立一個場景數組
到目前爲止,FoodTracker有一個單一的場景經過View Controller來管理一個屏幕,它能添加和評級一個新的菜譜:這個菜譜場景。如今是時候建立一整個菜譜的列表。幸運的是,iOS有一個強大的內置類叫table view(UITableView)用來顯示具體的滾動列表.app
一個table view經過UITableViewController來管,它是UIViewControllrt的子類,用來處理table view相關的邏輯。你將基於一個table view controller建立新的場景。ide
添加一個table view場景到storyboard模塊化
1.打開storyboard,Main.storyboard函數
2.打開Object library工具
3.在Object library中,找到一個Table View Controller對象
4.拖動Table View Controller到畫布中
請注意不要混淆table view和table view controller,請選擇table view controller
你如今有兩個場景,一個用來顯示菜譜列表,一個用來顯示新的菜譜。用戶啓動app時,首先看到的是菜譜列表,因此告訴Xcode你的table view controller做爲第一個場景。
設置table view controller做爲初始場景
1.從原先的菜譜場景中,拖動storyboard入口點到table view controller
table view controller做爲在storyboard中的初始view,使它成爲app啓動後的第一個場景。
檢查站:執行你的app。你如今是能看到一個空的table view。有一些水平的分割線來分隔成行,但裏面沒有內容
接着,你須要改變一些table view的設置。
配置table view
1.在storyboard中,打開outline view
2.在outline view中,選中Table View
table view嵌套在Table View Controller Scene>Table View Controller中。您可能須要點擊旁邊的三角形來顯示嵌套視圖。
3.選中table view的狀況下,打開Size inspector
4.在Size inspector中,找到Row Height標籤,而後輸入90,按下Return
你將返回到工做,接下來咱們須要設置table view cell的界面
設計自定義Table View Cells
這table view中的每一行,經過UITableViewCell來管理,用來響應繪製它們的內容。 Table view cells伴隨各類預約義的行爲和默認cell風格。默認cell風格大多數狀況下適用,但你有更多的內容,須要顯示類每個cell中,你須要自定義cell的風格,這個相似android的listvew中的item。
建立UITableViewCell的子類
1.選擇File > New > File (或按Command-N)
2.在對話框的左邊,選擇iOS下的Source
3.選擇Cocoa Touch Class,點擊Next
4.在Class標籤旁,輸入Meal
5.在Subclass of標籤旁,選擇UITableViewCell
6.確保語言爲Swift
7.點擊Next
確保app已選,tests未選擇
8.點擊Create
Xcode會建立一個文件MealTableViewCell.swift
如今打開你的storyboard
若是你看到table view controller場景,你注意到它僅顯示一個單一的cell
這個cell是其餘cells的原型,你只要設計和給定這個cell的行爲,那麼全部的table view中的cell都會同樣。但首先,你須要作一些配置。鏈接table view cell到int剛建立的自定義cell子類
爲你的table view配置一個自定義的cell
1.在outline view中, 選擇able View Cell
cell嵌套在Table View Controller Scene > Table View Controller > Table View下。
2.選中table view cell的狀況下,打開Attributes inspector
3.在Attributes inspector中,找到Identifier標籤,而後輸入MealTableViewCell,按下return
4.在Attributes inspector中,找到Selection標籤,選後選擇None。
當你選擇這個時,表示用戶點擊cell時,不會高亮(highlight)
5.打開 Size inspector
6.在 Size inspector中,找到Row Height標籤,輸入93,按下回車。
確保Custom複選框被選中
7.打開Identity inspector。
8.在Identity inspector中,找到標籤Class而後選擇MealTableViewCell
cell配置完畢,接下來咱們要在storyboard中設計它的自定義UI。它包含菜譜的名稱,照片,評級,看起來以下圖:
因此,咱們須要一個label,image view,一個rating control這三個UI控件。這些控件咱們都是在前面章節用過的。
設計自定義table cell的界面
1.選擇 Editor > Canvas > Show Bounds Rectangles來顯示UI元素邊界,在table cell中使元素更容易對齊
2.在Object library中,找到Image View對象,而後拖動到table cell中
3.拖動和調整image view,讓它變成一個方形,使其cell的左部,頂部,底部平齊。
4.確認添加先前章節的默認圖片是否在項目中,若是沒有你須要從新添加進來。
5.選中image view的狀況下,打開Attributes inspector
6.在Attributes inspector中,找到標籤Image選擇defaultPhoto
7.在Object library找到label對象,並拖動到table cell中
8.拖動lable,讓它靠經image view的右邊,而且讓頂部邊距對齊
9.調整label的大小,讓右邊距延伸到table cell的右邊距
10.在Object library中,找到View對象,並拖動到table cell中
11.選擇View的狀況下,打開 Size inspector
12.在Size inspector中,輸入44的Height和240的Width。按下return
13.拖動View到cell中,讓它位於標籤的底部,並和標籤左邊對齊
14.選中View的狀況下,打開Identity inspector
15.在Identity inspector中,找到Class標籤,並選擇RatingControl
16.在View選中的狀況下,打開Attributes inspector
17.Attributes inspector找到標籤Interaction,而後在User Interaction Enabled取消選中
對於咱們先前設計的rating control類是能夠交互的,但因爲此場景中,咱們只用來顯示,因此不容許交互。根據使用的場景不一樣,咱們須要作出一些響應的調整。
如今咱們的UI看起來以下:
檢查站:運行咱們的App。table view cells如今看起來很高。即便咱們添加了UI元素到table view cells,爲何他們仍是空的?
在storyboard中,一個table view用於配置和顯示靜態數據(storyboard中提供的)或動態數據(table view controller邏輯代碼中)。table view默認使用動態數據,因此咱們在storyboard配置的靜態數據,是顯示不出來的。直到咱們實現了它背後的數據模型。接下來咱們須要使用assistant editor了。
預覽界面
1.在Xcode工具欄上點擊assistant editor
2.若是你想要更多工做空間,能夠展開項目導航和實用工具區域
3.在 editor selector bar,從Automatic切換到Preview > Main.storyboard (Preview),接下來你的Xcode窗口應該看起來以下圖:
若是你發現了不對勁,請確保你選擇的是 table view場景
添加Images到你的項目
接下來咱們須要添加簡單的圖片到項目中。當載入初始化菜譜數據到你的app中時,咱們會使用這些圖像。你能找到簡單的Images,能夠點擊這裏下載或使用你本身的圖片
添加圖像
1.返回到 standard editor
2.選擇Images.xcassets
3.在底部左下角,點擊(+)號,而後從彈出的菜單中選中New Folder
4.雙擊文件夾,重命名爲Sample Images
5.選擇文件夾的狀況下,底部左下角,點擊(+)號,而後選擇New Image Set
6.雙擊這個圖片,重命名爲你記得的名字
7.在電腦中,選擇你想要拖動的圖片
8.拖動在2x槽中。
重複5-8步驟,你能夠添加不少圖片,只要你喜歡,本例中只添加三張圖片
鏈接Table Cell UI到代碼中
在table view cells顯示動態數據以前,在 MealTableViewCell.swift中
你須要在views和代碼之間建立一個outlet鏈接,用來展現table view cell
鏈接views到MealTableViewCell.swift代碼中
1.在storyboard中,選中table view cell中的標籤
2.打開assistant editor
3.若是你想要更多的工做空間,能夠展開項目導航和實用區域
4.在e editor selector bar中,從Preview切換到Automatic > MealTableViewCell.swift
5.在MealTableViewCell.swift中。找到class這行:
class MealTableViewCell: UITableViewCell {
6.在class下,添加以下代碼:
// MARK: Properties
7.拖動畫布中的標籤到代碼中,在剛添加的註釋下方,鬆開:
8.在出現的對話框中,Name標籤旁,鍵入nameLabel
9.點擊Connect
10.在storyboard中,選擇table view cell下的Image View
11.從畫布中拖動Image View到代碼中,而後在MealTableViewCell.swift中nameLabel屬性下方鬆開
12.在出現的對話框中,的Name標籤旁邊,鍵入photoImageView,而後Connect
13.打開storyboard,選擇table view cell中的rating control
14.拖動rating control到代碼中,在photoImageView
屬性下方鬆開
15.在對話框中的Name標籤旁邊,輸入ratingControl,而後點擊Connect
在MealTableViewCell.swift中,你的outlets,以下:
@IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var photoImageView: UIImageView! @IBOutlet weak var ratingControl: RatingControl!
載入初始化數據
爲了顯示table cells中真實的數據,你須要寫代碼來載入這個數據。在這一點上,你有一個菜譜的數據模型:Meal類。你須要一個菜譜列表的數據。view controller將管理顯示的菜譜列表view,在顯示UI以前,須要一個數據模型的引用。首先建立一個custom table view controller的子類來管理菜譜列表場景。
建立UITableViewController的子類
1.選擇 File > New > File(或按下Command+N)
2.在彈出的對話框中,選擇iOS下的Source,而後選擇Cocoa Touch Class
3.點擊Next
4.在Class標籤旁,輸入Meal
5.在Subcalss of旁,選擇UITableViewController
6.確保Also create XIB file選項未勾選
7.確保語言爲Swift
8.點擊Next
9.點擊Create
Xcode會建立一個MealTableViewController.swift類
在自定義的子類中,你如今能自定義屬性來存儲Meal對象列表。Swift標準庫中,包含了Array這個結構體(structure)。這裏咱們須要用到它
載入初始化數據
1.返回到standard editor
2.打開 MealTableViewController.swift
3.在MealTableViewController.swift中的class這行下方添加以下代碼:
// MARK: Properties var meals = [Meal]()
這個代碼是聲明一個屬性並初始化默認值爲一個空的Meal對象數組(array)。使用var表示數據是可變的,由於咱們會改變這個數據中的元素。
4.在MealTableViewController.swift中,viewDidLoad()方法下方,添加以下代碼:
func loadSampleMeals() {
}
這是一個輔助方法用來來加載樣本數據到app中。
5.在loadSampleMeals()方法中,添加如下代碼,來建立一些Meal對象,你能命名和評級這些菜譜樣本,下面是一些例子代碼:
let photo1 = UIImage(named: "meal1.jpg")! let meal1 = Meal(name: "Caprese Salad", photo: photo1, rating: 4)! let photo2 = UIImage(named: "meal2.jpg")! let meal2 = Meal(name: "Chicken and Potatoes", photo: photo2, rating: 5)! let photo3 = UIImage(named: "meal3.jpg")! let meal3 = Meal(name: "Pasta with Meatballs", photo: photo3, rating: 3)!
確保圖像的名字和你項目中的名字匹配
6.在建立Meal對象後,添加它們到meals數組中
meals += [meal1, meal2, meal3]
7.找到 viewDidLoad()方法,模版代碼實現以下:
override func viewDidLoad() { super.viewDidLoad() // Uncomment the following line to preserve selection between presentations // self.clearsSelectionOnViewWillAppear = false // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem() }
這個方法的模版實現,包含了註釋。這樣的代碼註釋提供有用的提示和源代碼文件的上下文信息,但本例中咱們不須要這些註釋
8.在viewDidLoad()方法中,刪除註釋,在super.viewDidLoad()下方添加如下代碼來載入樣本菜譜數據:
// Load the sample data. loadSampleMeals()
這個方法中,當view載入時,咱們須要載入數據。咱們寫一個載入方法,使代碼更加模塊化和可讀性。
咱們的viewDidLoad()方法看起來以下:
override func viewDidLoad() { super.viewDidLoad() // Load the sample data. loadSampleMeals() }
咱們的loadSampleMeals()方法,看起來以下:
func loadSampleMeals() { let photo1 = UIImage(named: "meal1.jpg")! let meal1 = Meal(name: "Caprese Salad", photo: photo1, rating: 4)! let photo2 = UIImage(named: "meal2.jpg")! let meal2 = Meal(name: "Chicken and Potatoes", photo: photo2, rating: 5)! let photo3 = UIImage(named: "meal3.jpg")! let meal3 = Meal(name: "Pasta with Meatballs", photo: photo3, rating: 3)! meals += [meal1, meal2, meal3] }
顯示數據
在這一點上,咱們自定義的table view controller子類MealTableViewController,有一個可變的數組,裏面預置一些樣本菜譜,如今你須要在UI中顯示數據
爲了顯示動態數據,一個table view須要兩個重要的幫助:1個數據源和一個委託。table view數據源,就如它的名字同樣,用來提供table view須要顯示的數據。一個table view委託幫助table view管理cell選擇,行高,以及顯示數據的其餘方法。默認UITableViewController和他的子類採用必要的協議來使table view controller的子類,既是一個數據源(UITableViewDataSource協議)又是一個委託(
UITableViewDelegate協議
)。你的工做就是實現相應的協議方法,來讓你的table view有正確的行爲。
一個table view功能需求兩個table view數據源方法
func numberOfSectionsInTableView(tableView: UITableView) -> Int func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
numberOfSectionsInTableView()方法是告訴table view顯示多少部件。部件是table views中可見的一組cells,但table view中有大量數據時,特別有用。但本例中實現起來很是簡單。
顯示table view中的部件
1.在MealTableViewController.swift中,找到numberOfSectionsInTableView()數據源方法。模版實現以下:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int { // #warning Incomplete implementation, return the number of sections return 0 }
2.改變返回值爲1,並移除註釋
override func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 }
上面的代碼,使table view顯示一個部件,而不是0。
下面的數據源方法tableView(_:numberOfRowsInSection:),告訴table view中的一個部件顯示顯示多少行。一個table view中默認有一個單一的部件,每個菜譜在部件中都佔一行。意思就是數組中有多少的Meal對象就會顯示多少行。
在table view中返回行數
1.在 MealListTableViewController.swift中,找到tableView(_:numberOfRowsInSection:)數據源方法。模版實現以下:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // #warning Incomplete implementation, return the number of rows return 0 }
你想要返回菜譜的數量,可使用Array中count屬性。
2.改變tableView(_:numberOfRowsInSection:)數據源方法來返回合適的函數,並移除警告註釋
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return meals.count }
最後的數據源方法tableView(_:cellForRowAtIndexPath:),用來配置和提供一個cell來顯示給定的行數。每一行都有一個cell,該cell肯定內容和佈局
對於table views只有小部分行數的狀況,全部行數可能只在屏幕上顯示一次。因此這個方法在 table view中的每行調用。但若是table view有不少不少行須要顯示,那麼在給定的時間內,只顯示總數的一小部分。
對於在table view任意給定的行數,你經過獲取相應的meals數組中的Meal來配置cell,而後設置cell的屬性和Meal類中相應的值
配置和顯示cells
1.在 MealListTableViewController.swift中,找到並取消tableView(_:cellForRowAtIndexPath:)數據源函數的註釋。模版方法實現以下:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath) as! UITableViewCell // Configure the cell... return cell }
模版執行了幾個任務。它經過placeholder的標識符訪問table view中的cell ,添加註釋 接下來咱們須要配置cell,而後return cell。爲了確保你的app正常工做,你須要改變placeholder標識符,到你早期在MealTableViewCell中設置的cell原型,而後添加代碼類配置cell
2.繼續添加如下代碼到方法中
// Table view cells are reused and should be dequeued using a cell identifier. let cellIdentifier = "MealTableViewCell"
這是建立一個標識符常量,就是你在storyboard設置的那個
3.更新placeholder標識符到你在storyboard設置的標識符。咱們應該修改上述方法中第二行的代碼:
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! UITableViewCell
4.由於你已經建立了一個自定義的cell類,改變cell的類型爲你自定義的cell子類,把UITableViewCell改爲MealTableViewCell,繼續修改方法的第二行代碼
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! MealTableViewCell
5.接着在下面添加以下代碼
// Fetches the appropriate meal for the data source layout. let meal = meals[indexPath.row]
這行代碼用來獲取合適的meal
6.刪除// Configure the cell註釋,添加以下代碼:
cell.nameLabel.text = meal.name cell.photoImageView.image = meal.photo cell.ratingControl.rating = meal.rating
爲每個UI元素賦值
最終tableView(_:cellForRowAtIndexPath:)函數看起來以下:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { // Table view cells are reused and should be dequeued using a cell identifier. let cellIdentifier = "MealTableViewCell" let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! MealTableViewCell // Fetches the appropriate meal for the data source layout. let meal = meals[indexPath.row] cell.nameLabel.text = meal.name cell.photoImageView.image = meal.photo cell.ratingControl.rating = meal.rating return cell }
最後一步是在UI中顯示數據,並鏈接定義在MealTableViewController.swift中的代碼到storyboard中的菜譜列表場景
table view controller場景指向MealTableViewController.swift
1.打開你的storyboard
2.選擇table view controller(能夠用過scene dock選擇)
3.打開Identity inspector
4.在Identity inspector中,在Class標籤旁,選擇MealTableViewController
檢查站:運行你的應用程序。你會發現 table view cells 和狀態欄有一些重疊,咱們將在下一章修復
準備Meal場景的導航
當你準備實現應用程序FoodTracker的導航時,你須要刪除和替換一些代碼和UI,由於你將再也不須要。
清理項目中未使用的部分
1.打開你的storyboard,而後看着meal場景,你的場景應該和下面差很少:
2.在meal場景,選中Meal Nam這個標籤
,而後按下Delete鍵刪除它
3.打開ViewController.swift
4.在ViewController.swift
, 找到textFieldDidEndEditing(_:)方法
func textFieldDidEndEditing(textField: UITextField) { mealNameLabel.text = textField.text }
5.刪除文本屬性設置代碼
mealNameLabel.text = textField.text
很快你將替換新的實現
6.在ViewController.swift
, 找到mealNameLabel
outlet並刪除它
@IBOutlet weak var mealNameLabel: UILabel!
由於你如今有2個view controllers,咱們須要爲ViewController.swift取一個更有意義的名字
重命名 ViewController.swift文件
1.在項目導航中,選擇ViewController.swift,而後按下回車
Xcode會讓你輸入一個新的名字
2.重命名這個文件爲MealViewController.swift。而後按下Return
3.在 MealViewController.swift,找到類聲明
class ViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
4.改變類名爲MealViewController
class MealViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
5.在文件頂部的註釋中,把之前的ViewController.swift
修更名字爲MealViewController.swift
6.打開storyboard
7.選擇meal場景
8.在選中meal場景的狀況下,打開Identity inspector
9.在 Identity inspector中,找到Class標籤,把ViewController修改成MealViewController
檢查站:運行你的應用程序。一切都應該像之前同樣工做。你可能會在Xcode中看到警告,說沒有方法到達meal場景,不要擔憂咱們將在下一章講解