馬上著手開發(fā) iOS 應(yīng)用程序 (八) - 創(chuàng)建表格視圖

重要:這是針對(duì)于正在開發(fā)中的API或技術(shù)的預(yù)備文檔(預(yù)發(fā)布版本)。蘋果提供這份文檔的目的是幫助你按照文中描述的方式對(duì)技術(shù)的選擇及界面的設(shè)計(jì)開發(fā)進(jìn)行規(guī)劃。這些信息有可能發(fā)生變化,因此根據(jù)本文檔的軟件開發(fā)應(yīng)當(dāng)基于最終版本的操作系統(tǒng)和文檔進(jìn)行測試。該文檔的新版本或許會(huì)隨著API或相關(guān)技術(shù)未來的發(fā)展而進(jìn)行更新。

翻譯自蘋果官網(wǎng):

https://developer.apple.com/library/ios/referencelibrary/GettingStarted/DevelopiOSAppsSwift/Lesson7.html#//apple_ref/doc/uid/TP40015214-CH8-SW1

在本課中,創(chuàng)建第二個(gè)場景,它基于 table view(表格視圖) 并列出用戶的所有食物。將會(huì)創(chuàng)建自定義表格單元格來顯示每一份食物,最后它應(yīng)該像這樣:

[圖片上傳失敗...(image-a0b7ee-1608214888461)]

學(xué)習(xí)目標(biāo)

在課程的最后,你將能夠:

  • 創(chuàng)建第二個(gè) storyboard 場景
  • 理解 table view 的關(guān)鍵部分。
  • 創(chuàng)建和設(shè)計(jì)一個(gè)自定義 table view cell(表格視圖單元格)。
  • 理解 table view 的代理和數(shù)據(jù)源。
  • 使用一個(gè)數(shù)組來存放和使用數(shù)據(jù)。
  • 在 table view 中顯示動(dòng)態(tài)數(shù)據(jù)。

創(chuàng)建開始場景

目前,F(xiàn)oddTracker app 有單個(gè)被視圖控制器管理的食物場景,用戶可以在其中添加和評(píng)價(jià)食物?,F(xiàn)在是時(shí)候創(chuàng)建新的場景來顯示所有食物的列表。幸運(yùn)的是,iOS 有個(gè)非常強(qiáng)大名叫 table view(UITableView) 的內(nèi)置類,它被特別設(shè)計(jì)用來顯示一個(gè)滾動(dòng)列表。

table view 被 UITableViewController 管理,它是 UIViewController 的子類專門用來處理 table view 相關(guān)的邏輯。你將會(huì)創(chuàng)建一個(gè)新的基于它的場景。

添加一個(gè)帶 table view 的場景到 storyboard 中
  1. 打開 Main.storyboard。
  2. 在實(shí)用工具區(qū)中打開對(duì)象庫。(或者,選擇 View > Utilities > Show Object Library。)
  3. 在對(duì)象庫中,找到 Table View Controller 對(duì)象。
  4. 拖動(dòng) Table View Controller 放在畫板中食物場景的左邊。
    確保拖動(dòng) table view controller 而不是 table view 對(duì)象。table view 是被 table view controller 管理的對(duì)象,但是需要的是控制器而不是單個(gè) table view,所以找到 table view controller 并拖動(dòng)它到畫板中。

現(xiàn)在有兩個(gè)場景了,一個(gè)用于顯示食物列表,一個(gè)是用于添加新的食物。

讓食物列表成為用戶啟動(dòng)你的 app 后第一個(gè)看到的場景是很有意義的,所以設(shè)置 table view controller 作為首要場景告訴 Xcode 你的意圖。

設(shè)置 table view controller 作為啟動(dòng)場景
  1. 如果想要更多空間來工作,點(diǎn)擊 Xcode 工具欄中的 Navigator 和 Utilities 按鈕來收縮項(xiàng)目導(dǎo)航和實(shí)用工具區(qū)。

    [圖片上傳失敗...(image-dfbe7d-1608214888461)]

  2. 從食物場景中拖動(dòng) storyboard entry point 到 table view controller 中。

    [圖片上傳失敗...(image-4209ae-1608214888461)]

    在你的 storyboard 中設(shè)置 table view controller 作為啟動(dòng)視圖控制器,這讓它在 app 啟動(dòng)的時(shí)候成為第一個(gè)加載的場景。

    [圖片上傳失敗...(image-2750a3-1608214888461)]

檢驗(yàn):運(yùn)行你的 app。取代之前那個(gè)擁有文本框,image view 和評(píng)分控件的食物場景,你應(yīng)該看到一個(gè)空的 table view - 它被水平分割線分成一行行但是各行并沒有內(nèi)容。

[圖片上傳失敗...(image-402f0d-1608214888461)]

你需要修改 table view 的設(shè)置這樣才能在你的 app 中使用。

配置 table view
  1. 在你的 storyboad 中,打開大綱視圖。

  2. 在大綱視圖中,選擇 Table View。

    table view 嵌套在 Table View Controller Scene > Table View Controller 的下面。你或許要點(diǎn)擊旁邊的三角形才能看到嵌套的 table view。

    [圖片上傳失敗...(image-be797c-1608214888461)]

  3. 選中 table view,在實(shí)用工具區(qū)打開尺寸檢查器。

    選中檢查器選擇欄左邊第五個(gè)按鈕打開尺寸檢查器。它能讓你編輯 storyboard 中對(duì)象的尺寸和位置。

    [圖片上傳失敗...(image-503b71-1608214888461)]

  4. 在尺寸檢查器中,找到名為 Row Heigh 的區(qū)域輸入 90。回車確認(rèn)。

繼續(xù)設(shè)計(jì)單元格界面,稍后將會(huì)回來使用這個(gè) table view。

設(shè)計(jì)自定義 table view 單元格

table view 中單獨(dú)的一行是由 UITableViewCell 管理的。單元格有不同的預(yù)定義的功能以及一些默認(rèn)的樣式。默認(rèn)的單元格樣式在很多情況下都很棒,但是在本課中需要在單元格中顯示更多內(nèi)容,而這超過了默認(rèn)樣式所允許的了,所以需要自定義一個(gè)單元格樣式。

創(chuàng)建 UITableViewCell 的子類
  1. 選擇 File > New > File(或者按 Command-N)。
  2. 在出現(xiàn)的對(duì)話框左邊,選擇 iOS 下面的 Source。
  3. 選擇 Cocoa Touch Class,然后點(diǎn)擊 Next。
  4. 在 Class 區(qū)域,輸入 Meal。
  5. Subclass Of 區(qū)域,選擇 UITableViewCell。
    類的標(biāo)題自動(dòng)修改為 MealTableViewCell。從命名很清楚的知道你在創(chuàng)建一個(gè)自定義 table view 單元格,所以讓它成為新的名字。
  6. 確保語言選項(xiàng)設(shè)置為 Swift 。
  7. 點(diǎn)擊 Next。
    默認(rèn)保存位置是你的項(xiàng)目目錄。
    Group 選項(xiàng)默認(rèn)是你的 app 名字,F(xiàn)oodTracker。
    在 Targets 區(qū)域,確保你的 app 選中而 targets 不選中。
  8. 讓剩下的使用默認(rèn)選項(xiàng),然后點(diǎn)擊 Create。
    Xcode 創(chuàng)建了 MealTableViewCell.swift 文件并定義了 MealTableViewCell 類。

現(xiàn)在,打開 storyboard。
你會(huì)注意到 storyboard 中 table view 只顯示了單個(gè)單元格。

[圖片上傳失敗...(image-9063d7-1608214888461)]

這個(gè)單元格代表其他單元格的原型;其中定義的設(shè)計(jì)和功能會(huì)被 table view 的其他單元格使用。但是首先,需要做一點(diǎn)配置。連接場景中的單元格和剛才創(chuàng)建的自定義單元格子類。

為 table view 創(chuàng)建一個(gè)自定義單元格
  1. 在大綱視圖中,選擇 Table View Cell。
    單元格嵌套在 Table View Controller Scene > Table View Controller > Table View 下面。你或許需要展開這些對(duì)象才能看到單元格。

    [圖片上傳失敗...(image-e1f748-1608214888461)]

  2. 選中單元格,在實(shí)用工具區(qū)打開屬性檢查器。

  3. 在屬性檢查器中,找到叫 Identifier 的區(qū)域輸入 MealTableViewCell?;剀嚧_認(rèn)。
    這是很重要的一步 - 之后你就知道為什么了。

  4. 在屬性檢查器中,找到名為 Selection 的區(qū)域選擇 None。
    使用這個(gè)選項(xiàng),當(dāng)用戶點(diǎn)擊單元格的時(shí)候不會(huì)產(chǎn)生視覺高亮的效果。

  5. 打開尺寸檢查器。

  6. 在尺寸檢查器中,找到名為 Row Height 的區(qū)域輸入 90。
    確保旁邊的 Custom 復(fù)選框選中了:

    [圖片上傳失敗...(image-3262a1-1608214888461)]

    回車確認(rèn)在 storyboard 中顯示了新的單元格高度。

  7. 打開識(shí)別(Identity)檢查器。
    回憶下識(shí)別檢查器讓你在 storyboard 中編輯一個(gè)對(duì)象的 Identity(標(biāo)識(shí)) 相關(guān)的屬性,例如對(duì)象屬于哪個(gè)類。

  8. 在識(shí)別檢查器中,找到名為 Class 的區(qū)域并選擇 MealTableViewCell。

    [圖片上傳失敗...(image-b5eaeb-1608214888461)]

配置完單元格,可以在 storyboard 中設(shè)計(jì)它的自定義界面。它包含食物的名字,照片和評(píng)分,應(yīng)該像這樣:

[圖片上傳失敗...(image-446087-1608214888461)]

為了達(dá)到這個(gè)效果,需要一個(gè)標(biāo)簽,一個(gè) image view 和一個(gè)評(píng)分控件。你可以復(fù)用在前一課中創(chuàng)建的評(píng)分控件。

設(shè)計(jì)自定義單元格的界面
  1. 選擇 Editor > Canvas > Show Bounds Rectangles 來顯示界面控件的邊界,讓控件在單元格中更容易對(duì)齊。

    [圖片上傳失敗...(image-e2c849-1608214888461)]

  2. 使用對(duì)象庫尋找一個(gè) Image View 對(duì)象并拖動(dòng)到單元格中。

  3. 拖動(dòng)和調(diào)整 image view 的尺寸讓它平行并填充單元格的左邊、頂部和底部。

    [圖片上傳失敗...(image-a3aff2-1608214888461)]

  4. 如果你在前一課沒有添加默認(rèn)的圖片到你的項(xiàng)目中,現(xiàn)在添加吧。

  5. 選中這個(gè) image view,在實(shí)用工具區(qū)打開屬性檢查器。

  6. 在屬性檢查器中,找到名為 Image 的區(qū)域并選擇 defaultPhoto。

    [圖片上傳失敗...(image-114f6b-1608214888461)]

  7. 使用對(duì)象庫來找到一個(gè)標(biāo)簽對(duì)象并拖動(dòng)到單元格中。

  8. 拖動(dòng)標(biāo)簽讓它接近 image view 的右邊并對(duì)齊表格單元格的頂部邊界。

    [圖片上傳失敗...(image-6317a8-1608214888461)]

  9. 調(diào)整標(biāo)簽的尺寸讓它的右邊伸展到單元格的右邊緣。

    [圖片上傳失敗...(image-614a94-1608214888461)]

  10. 使用對(duì)象庫找到一個(gè) View 對(duì)象并拖動(dòng)到單元格中。

  11. 選中這個(gè)視圖,在實(shí)用工具區(qū)打開尺寸檢查器。

  12. 在尺寸檢查器的 Height 區(qū)域輸入 44,Width 區(qū)域輸入 240?;剀嚧_認(rèn)。

  13. 拖動(dòng)視圖讓它在標(biāo)簽的下面并對(duì)齊標(biāo)簽的左邊緣。

    [圖片上傳失敗...(image-a4711e-1608214888461)]

  14. 選中視圖,打開識(shí)別檢查器。

  15. 在識(shí)別檢查器中,找到名為 Class 的區(qū)域選擇 RatingControl。

    [圖片上傳失敗...(image-c7b36b-1608214888461)]

    如果你沒有看到 RatingControl 作為彈出菜單的選項(xiàng),確保你在畫板(在前一張圖片中顯示了 resize handles 的那個(gè))中選擇了正確的界面控件。

  16. 選中視圖,打開屬性檢查器。

  17. 在屬性檢查器中,找到名為 Interaction 的區(qū)域并取消選中 User Interaction Enabled 復(fù)選框。
    之前設(shè)計(jì)的自定義評(píng)分控件類主要用于交互,但是在單元格中是不需要交互。所以當(dāng)它在這個(gè)環(huán)境中關(guān)閉用戶交互,這是很重要的。

我們的界面應(yīng)該像這樣:

[圖片上傳失敗...(image-5c5a6e-1608214888461)]

檢驗(yàn):運(yùn)行你的 app。單元格現(xiàn)在看起來更高了。但是即使你向單元格中添加了所有必須的界面控件,table view 還是像以前一樣是空的,為什么會(huì)那樣?

[圖片上傳失敗...(image-23e779-1608214888461)]

在一個(gè) storyboard 中,table view 可以配置用來顯示靜態(tài)的數(shù)據(jù)(storyboard 支持)或者動(dòng)態(tài)數(shù)據(jù)(table view controller 邏輯支持)。table view 默認(rèn)使用動(dòng)態(tài)數(shù)據(jù),所以你需要在代碼中加載數(shù)據(jù),這正是你希望做的-你僅僅需要實(shí)現(xiàn)這個(gè)行為。這意味著你在 storyboard 中提供的靜態(tài)內(nèi)容并不會(huì)在運(yùn)行時(shí)顯示,所以看不到 table view 的內(nèi)容 - 直到你實(shí)現(xiàn)了它之后的數(shù)據(jù)模型。

現(xiàn)在,使用輔助編輯器預(yù)覽界面。

預(yù)覽界面
  1. 點(diǎn)擊 Xcode 工具欄中的 Assistant 按鈕來打開輔助編輯器。

    [圖片上傳失敗...(image-c51733-1608214888461)]

  2. 如果想要更多空間來工作,通過點(diǎn)擊 Xcode 工具欄中的 Navigator 和 Utilities 按鈕來收縮項(xiàng)目導(dǎo)航和實(shí)用工具區(qū)。

    [圖片上傳失敗...(image-597a84-1608214888461)]

    同樣可以收縮大綱視圖。

  3. 在出現(xiàn)的輔助編輯器頂部的編輯器選擇欄中,把輔助編輯器從 Automatic 切換為 Preview > Main.storyboard(Preview)。

    [圖片上傳失敗...(image-faabb2-1608214888461)]

    你的 Xcode 窗口應(yīng)該像這樣:

    [圖片上傳失敗...(image-591f52-1608214888461)]

預(yù)覽界面看起來像預(yù)期的那樣。原型單元格界面看起來完成了。

注意

如果你在界面預(yù)覽中看到錯(cuò)誤的場景了,通過點(diǎn)擊場景 dock 確保選擇了 table view 場景。

添加圖片到項(xiàng)目中

下一步,往你的項(xiàng)目中添加示例圖片。當(dāng)加載初始食物數(shù)據(jù)到你的 app 時(shí)會(huì)用到這些圖片。

你可以在課程最后項(xiàng)目的 Images/ 文件夾中找到示例圖片,或者使用你自己的圖片。(確保使用的圖片的名字匹配稍后的代碼中的圖片名字。)

添加圖片到項(xiàng)目中
  1. 如果輔助編輯器打開了,點(diǎn)擊 Standard 按鈕返回標(biāo)準(zhǔn)編輯器。
    通過點(diǎn)擊 Xcode 工具欄中的 Navigator 和 Utilities 按鈕打開項(xiàng)目導(dǎo)航和實(shí)用工具區(qū)。

    [圖片上傳失敗...(image-dee0d3-1608214888461)]

  2. 在項(xiàng)目導(dǎo)航中,選擇 Assets.xcassets 來查看 asset catalog。
    回憶下 asset catalog 是 app 中儲(chǔ)存和組織圖片資源的地方。

  3. 點(diǎn)擊左下角的加號(hào)(+)從彈出菜單中選擇 New Folder。

  4. 雙擊文件夾名字將它重命名為 Sample Images。

  5. 選中文件夾,點(diǎn)擊左下角的加號(hào)(+)按鈕在彈出的菜單中選擇 New Image Set。

  6. 雙擊圖片集合名字把它重命名為寫代碼時(shí)容易記住的名字。

  7. 選擇你電腦中想要添加的圖片。

  8. 拖動(dòng)圖片并把它放到圖片集合的 2x 槽中。

重復(fù) 5-8 操作來盡可能添加你喜歡的圖片。剩下的課程假定你已經(jīng)有三張不同的圖片了。

[圖片上傳失敗...(image-df5a8-1608214888461)]

連接單元格界面和代碼

在單元格顯示動(dòng)態(tài)數(shù)據(jù)之前,你需要在視圖和單元格的代碼之間創(chuàng)建 outlet 連接。

連接視圖和 MealTableViewCell.swift 代碼
  1. 在你的 storyboard 中,選擇 table view cell 中的標(biāo)簽。

  2. 點(diǎn)擊 Xcode 工具欄中的 Assistant 按鈕來打開輔助編輯器。

    [圖片上傳失敗...(image-828ac7-1608214888461)]

  3. 如果想要更多空間來工作,通過點(diǎn)擊 Xcode 工具欄中的 Navigator 和 Utilities 按鈕來收縮項(xiàng)目導(dǎo)航和實(shí)用工具區(qū)。

    [圖片上傳失敗...(image-497fb-1608214888461)]

  4. 在輔助編輯器頂部的編輯器選擇欄中,將輔助編輯器從 Preview 切換到 Automatic > MealTableViewCell.swift。

    [圖片上傳失敗...(image-a52c35-1608214888461)]

    MealTableViewCell.swift 顯示在右邊編輯器中。

  5. 在 MealTableViewCell.swift 中,找到 class 行,應(yīng)該像這樣:

     class MealTableViewCell: UITableViewCell {
    
  6. 在 class 行的下面,添加如下注釋:

     // MARK: Properties
    
  7. 按住 Control 從畫板中的標(biāo)簽拖動(dòng)到右邊編輯器的代碼中,在 MealTableViewCell.swift 中剛添加的注釋的下面停止拖動(dòng)。

    [圖片上傳失敗...(image-23805e-1608214888461)]

  8. 在出現(xiàn)的對(duì)話框中輸入名字:nameLabel。
    忽略剩下的選項(xiàng)。你的對(duì)話框應(yīng)該像這樣:

    [圖片上傳失敗...(image-303f69-1608214888461)]

  9. 點(diǎn)擊 Connect。

  10. 在你的 storyboard 中,選擇 table view cell 中的 image view。

  11. 按住 Control 從畫板的 image view 拖動(dòng)到右邊編輯器的代碼中,在 MealTableViewCell.swift 的屬性 nameLabel 下面一行停止拖動(dòng)。

    [圖片上傳失敗...(image-232790-1608214888461)]

  12. 在出現(xiàn)的對(duì)話框中,輸入名字: photoImageView。
    忽略剩下的選項(xiàng)點(diǎn)擊 Connect。
    [圖片上傳失敗...(image-b268a4-1608214888461)]

  13. 在你的 storyboard 中,選擇 table view cell 中的評(píng)分控件。

  14. 按住 Control 從畫板的評(píng)分控件拖動(dòng)到顯示在右邊編輯器的代碼中,在 MealTableViewCell.swift 的 photoImageView 屬性的下面一行停止拖動(dòng)。

    [圖片上傳失敗...(image-b9541c-1608214888461)]

  15. 在出現(xiàn)的對(duì)話框中,輸入名字:ratingControl。
    忽略剩下的選項(xiàng)點(diǎn)擊 Connect。

    [圖片上傳失敗...(image-43bee1-1608214888461)]

MealTableViewCell.swift 中 outlets 應(yīng)該像這樣:

@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var photoImageView: UIImageView!
@IBOutlet weak var ratingControl: RatingControl!

加載初始數(shù)據(jù)

為了在你的單元格中顯示一些真實(shí)數(shù)據(jù),你需要編寫代碼來加載數(shù)據(jù)。此刻,你有個(gè)食物的數(shù)據(jù)模型:Meal 類。你同樣需要保存這些食物的列表。很自然在自定義視圖控制器的子類中保存這些數(shù)據(jù)。視圖控制器會(huì)管理視圖來顯示食物列表,后面會(huì)保存一個(gè)數(shù)據(jù)模型的引用。

首先,創(chuàng)建一個(gè)自定義控制器子類來管理食物列表場景。

創(chuàng)建 UITableViewController 的子類
  1. 選擇 File > New > File(或者按 Command-N)。

  2. 在出現(xiàn)的對(duì)話框左邊,選擇 iOS 下面的 Source,之后選擇 Cocoa Touch Class。

  3. 點(diǎn)擊 Next。

  4. 在 Class 區(qū)域,輸入 Meal。

  5. 在 Subclass of 區(qū)域,選擇 UITableViewController。
    類的標(biāo)題相應(yīng)修改為 MealTableViewController。就讓它這樣。

  6. 確保 Also Create XIB file 選項(xiàng)沒有選中。

  7. 確保語言選項(xiàng)設(shè)置成了 Swift。

  8. 點(diǎn)擊 Next。

    默認(rèn)的保存位置是你的項(xiàng)目目錄。
    Group 選項(xiàng)默認(rèn)是你的 app 名字,F(xiàn)oodTracker。
    在 Target 區(qū)域,你的 app 被選中而 tests 未選中。

  9. 剩下都是默認(rèn)值,然后點(diǎn)擊 Create。

    Xcode 創(chuàng)建 MealTableViewController.swift,一個(gè)自定義 table view controller 子類的源代碼文件。

在自定義子類中,定義一個(gè)屬性來存儲(chǔ) Meal 對(duì)象的列表。Swift 標(biāo)準(zhǔn)庫包含一個(gè)叫 Array 的數(shù)據(jù)結(jié)構(gòu)非常適合記錄這種列表。

加載初始數(shù)據(jù)
  1. 如果輔助編輯器打開了,點(diǎn)擊 Standard 按鈕返回標(biāo)準(zhǔn)編輯器。

    點(diǎn)擊 Xcode 工具欄中的 Navigator 和 Utilities 按鈕展開項(xiàng)目導(dǎo)航和實(shí)用工具區(qū)。

    [圖片上傳失敗...(image-ce4aa9-1608214888461)]

  2. 打開 MealTableViewController.swift。

  3. 在 MealTableViewController.swift 的 class 行下面,添加如下代碼:

     // MARK: Properties
    
     var meals = [Meal]()
    

    代碼定義了 MealTableViewController 的一個(gè)屬性并將它初始化默認(rèn)的值(一個(gè)空的食物對(duì)象數(shù)組)。通過設(shè)置 meals 為一個(gè)變量而不是常量,讓數(shù)組可變化,這意味著你可以在初始化它之后添加對(duì)象。

  4. 在 MealTableViewController.swift 文件的 viewDidLoad() 方法的后面,添加如下方法:

     func loadSampleMeals() {
     }
    

    這是一個(gè)幫助方法用來加載 app 的示例數(shù)據(jù)。

  5. 在 loadSampleMeals() 方法里面,添加代碼來創(chuàng)建一些 Meal 對(duì)象。你可以憑著自己喜歡來給這些示例食物命名和設(shè)置評(píng)分,當(dāng)然,這里是一些例子:

     let photo1 = UIImage(named: "meal1")!
     let meal1 = Meal(name: "Caprese Salad", photo: photo1, rating: 4)!
      
     let photo2 = UIImage(named: "meal2")!
     let meal2 = Meal(name: "Chicken and Potatoes", photo: photo2, rating: 5)!
      
     let photo3 = UIImage(named: "meal3")!
     let meal3 = Meal(name: "Pasta with Meatballs", photo: photo3, rating: 3)!
    

確保項(xiàng)目中圖片名字匹配你在代碼中寫的名字。

  1. 在創(chuàng)建完食物對(duì)象后,使用如下代碼添加到 meals 數(shù)組中。

     meals += [meal1, meal2, meal3]
    
  2. 找到 viewDidLoad() 方法。默認(rèn)模板實(shí)現(xiàn)如下:

     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()
     }
    

    當(dāng)創(chuàng)建 MealTableViewController.swift 時(shí)候 Xcode 默認(rèn)插入一些注釋。本課中不需要它們。

  3. 在 viewDidLoad() 方法中,刪除注釋并替換為這些代碼用來在 super.viewDidLoad() 后面來加載示例食物數(shù)據(jù):

     // Load the sample data.
     loadSampleMeals()
    

    調(diào)用剛才添加的幫助方法用來當(dāng)視圖加載的時(shí)候加載數(shù)據(jù)。你分割功能到定義的方法來讓代碼更模塊化和可讀。

    viewDidLoad() 方法應(yīng)該像這樣:

     override func viewDidLoad() {
         super.viewDidLoad()
         
         // Load the sample data.
         loadSampleMeals()
     }
    

    loadSampleMeals() 方法應(yīng)該像這樣:

     func loadSampleMeals() {
         let photo1 = UIImage(named: "meal1")!
         let meal1 = Meal(name: "Caprese Salad", photo: photo1, rating: 4)!
         
         let photo2 = UIImage(named: "meal2")!
         let meal2 = Meal(name: "Chicken and Potatoes", photo: photo2, rating: 5)!
         
         let photo3 = UIImage(named: "meal3")!
         let meal3 = Meal(name: "Pasta with Meatballs", photo: photo3, rating: 3)!
         
         meals += [meal1, meal2, meal3]
     }
    

檢驗(yàn):通過選擇 Product > Build 來運(yùn)行你的項(xiàng)目。編譯應(yīng)該沒有錯(cuò)誤。

重要:

如果運(yùn)行出現(xiàn)編譯問題,確保項(xiàng)目中的圖片名字確實(shí)匹配代碼中的名字。

顯示數(shù)據(jù)

此刻,你的自定義控制器子類, MealTableViewController,有個(gè)可變的數(shù)組包含一些示例食物?,F(xiàn)在你需要在界面上顯示這些真實(shí)數(shù)據(jù)。

為了顯示動(dòng)態(tài)數(shù)據(jù),table view 需要兩個(gè)很重要的幫手:數(shù)據(jù)源和代理。數(shù)據(jù)源,就像名字說的,提供給 table view 需要顯示的數(shù)據(jù)。代理幫助 table view 管理單元格選中、行高度和其他顯示數(shù)據(jù)相關(guān)功能。UITableViewController 和它的子類默認(rèn)遵循了必要的協(xié)議來讓它既是一個(gè)數(shù)據(jù)源(UITableViewDataSource 協(xié)議)又是一個(gè)代理(UITableViewDelegate)。你的工作就是在 table view controller 中實(shí)現(xiàn)合適的協(xié)議方法這樣 table view 才能有正確的功能。

一個(gè)正常工作的 table view 需要三個(gè)數(shù)據(jù)源方法。

    func numberOfSectionsInTableView(tableView: UITableView) -> Int
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell

第一個(gè)是 numberOfSectionsInTableView(_:),它告訴 table view 有多少個(gè)區(qū)域用來顯示。Sections 是 table views 中單元格視覺分組,尤其在 table views 有很多數(shù)據(jù)時(shí)候有用。一個(gè)簡單的 table view 例如我們 app 的,僅僅需要顯示單個(gè)組,所以 numberOfSectionsInTableView(_:) 數(shù)據(jù)源方法的實(shí)現(xiàn)很簡單。

在 table view 中顯示一個(gè)組
  1. 在 MealTableViewController.swift 中,找到 numberOfSectionsInTableView(_:) 數(shù)據(jù)源方法。模板默認(rèn)實(shí)現(xiàn)如下:

     override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
         // #warning Incomplete implementation, return the number of sections
         return 0
     } 
    
  2. 從0修改返回值為1,并且刪除警告注釋。

     override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
         return 1
     }
    

    代碼讓 table view 顯示 1 個(gè)區(qū)域而不是0個(gè)。移除 #warning Incomplete implementation 注釋因?yàn)槟阋呀?jīng)完成了實(shí)現(xiàn)。

下一個(gè)數(shù)據(jù)源方法, tableView(_:numberOfRowsInSection:) 告訴 table view 在一個(gè)給定組中顯示多少行。每份食物應(yīng)該在組中有屬于它自己的行,這意味著行的數(shù)量應(yīng)該等于 meals 數(shù)組中 Meal 對(duì)象的數(shù)量。

返回 table view 中行的數(shù)量
  1. 在 MealTableViewController.swift 中,找到 tableView(_:numberOfRowsInSection:) 數(shù)據(jù)源方法。模板默認(rèn)實(shí)現(xiàn)如下:

     override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         // #warning Incomplete implementation, return the number of rows
         return 0
     }
    

    返回你擁有的食物數(shù)量。Array 有個(gè)屬性名叫 count 并且返回 array 中對(duì)象的數(shù)量,所以行的數(shù)量是 meals.count。

  2. 修改 tableView(_:numberOfRowsInSection:) 數(shù)據(jù)源方法來返回合適行的數(shù)量,移除警告注釋。

     override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         return meals.count
     }
    

最后的數(shù)據(jù)源方法,tableView(_:cellForRowAtIndexPath:),配置單元格來顯示給定的行數(shù)據(jù)。table view 的每一行對(duì)應(yīng)一個(gè)單元格,那個(gè)單元格決定行中顯示的內(nèi)容以及怎么布局。

當(dāng) table views 有很少行的時(shí)候,所有的行或許同時(shí)都顯示在屏幕上,所以為 table view 的每一行調(diào)用這個(gè)方法。但是當(dāng) table views 有很多行,同時(shí)只能顯示所有中的一小部分。只為正在顯示的行請(qǐng)求數(shù)據(jù)是很有效率的,那正是 tableView(_:cellForRowAtIndexPath:) 允許 table view 做的。

為任何 table view 中給定的行,通過取 Meals 數(shù)組中對(duì)應(yīng)的 Meal 對(duì)象來配置單元格,使用 Meal 對(duì)象的屬性值設(shè)置單元格中對(duì)應(yīng)的控件。

在 table view 中配置和顯示單元格
  1. 在 MealTableViewController.swift 中,找到并取消 tableView(_:cellForRowAtIndexPath:) 數(shù)據(jù)源方法的注釋。(為了取消方法的注釋,移除環(huán)繞它的 /* 和 */ 字符。)
    當(dāng)你做了這些,模板默認(rèn)實(shí)現(xiàn)如下:

     override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
         let cell = tableView.dequeueReusableCellWithIdentifier("reuseIdentifier", forIndexPath: indexPath)
         
         // Configure the cell...
         
         return cell
     }
    

方法執(zhí)行一些操作。首先通過占位符標(biāo)識(shí)向 table view 請(qǐng)求一個(gè)單元格,添加一個(gè)注釋表示在這里配置單元格,之后返回單元格。

為了讓代碼工作,修改占位符標(biāo)識(shí)為之前你在原型單元格中設(shè)置的(MealTableViewCell),之后添加代碼來配置單元格。

  1. 在方法的開始添加代碼:

     // Table view cells are reused and should be dequeued using a cell identifier.
     let cellIdentifier = "MealTableViewCell"
    

    為 storyboard 中設(shè)置的標(biāo)識(shí)創(chuàng)建了一個(gè)常量。

  2. 更新占位符標(biāo)識(shí)為 storyboard 中設(shè)置的標(biāo)識(shí)。方法第二行代碼應(yīng)該像這樣:

     let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)
    
  3. 因?yàn)槭褂昧俗远x的單元格類,強(qiáng)轉(zhuǎn)單元格類型為你自定義單元格子類,MealTableViewCell,方法代碼的第二行改成這樣:

     let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! MealTableViewCell
    
  4. 之后添加如下代碼:

     // Fetches the appropriate meal for the data source layout.
     let meal = meals[indexPath.row]
    

    代碼從 meals 數(shù)組中拿到合適的食物。

  5. 刪除 // Configure the cell 注釋并在這個(gè)地方添加代碼:

     cell.nameLabel.text = meal.name
     cell.photoImageView.image = meal.photo
     cell.ratingControl.rating = meal.rating 
    

    使用 meal 中對(duì)應(yīng)數(shù)據(jù)來設(shè)置 table view 單元格的每個(gè)視圖。

tableView(_:cellForRowAtIndexPath:) 方法應(yīng)該像這樣:

    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
    }

最后一步就是連接 MealTableViewController.swift 中定義的代碼和 storyboard 的場景。

指向控制器到 MealTableViewController.swift
  1. 打開你的 storyboard。

  2. 點(diǎn)擊場景 dock 選擇 table view 控制器,可以看到它周圍有個(gè)藍(lán)色的框。

  3. 打開識(shí)別檢查器。

  4. 在識(shí)別檢查器中,找到名為 Class 的區(qū)域,選擇 MealTableViewController。

    [圖片上傳失敗...(image-4a89fa-1608214888461)]

檢驗(yàn):運(yùn)行 app。在 viewDidLoad() 方法中添加的列表項(xiàng)應(yīng)該顯示在 table view 中作為單元格。你會(huì)注意到 table view 單元格和狀態(tài)欄之間有一點(diǎn)重疊 - 在下一課會(huì)修復(fù)它的。

[圖片上傳失敗...(image-85ded9-1608214888461)]

準(zhǔn)備食物場景用于導(dǎo)航

在 FoddTracker app 中實(shí)現(xiàn)導(dǎo)航功能前,需要?jiǎng)h除一些不再需要的代碼和控件。

清理不需要的代碼和控件
  1. 打開 storyboard 查看食物場景。

    你的食物場景界面應(yīng)該像這樣:

    [圖片上傳失敗...(image-429f7c-1608214888461)]

  2. 在食物場景中,選擇 Meal Name 標(biāo)簽,按 Delete 鍵刪除它。

    堆棧視圖會(huì)合理調(diào)整剩余的控件位置。

    [圖片上傳失敗...(image-3883f7-1608214888461)]

  3. 打開 ViewController.swift。

  4. 在 ViewController.swift 中,找到 textFieldDidEndEditing(_:) 方法。

     func textFieldDidEndEditing(textField: UITextField) {
         mealNameLabel.text = textField.text
     }
    
  5. 刪除設(shè)置標(biāo)簽文本屬性那行。

     mealNameLabel.text = textField.text
    

    稍后會(huì)用新的實(shí)現(xiàn)來代替它。

  6. 在 ViewController.swift 中,找到 mealNameLabel outlet 并刪除它。

     @IBOutlet weak var mealNameLabel: UILabel!
    

因?yàn)楝F(xiàn)在項(xiàng)目中兩個(gè)視圖控制器了,給 ViewController.swift 一個(gè)更有意義的名字吧。

重命名 ViewController.swift 文件
  1. 在項(xiàng)目導(dǎo)航中,點(diǎn)擊 ViewController.swift 文件并按回車鍵。
    Xcode 讓你為文件輸入新的名字。

  2. 重命名文件為 MealViewController.swift,回車確認(rèn)。

  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. 點(diǎn)擊場景 dock 選中食物場景。

    [圖片上傳失敗...(image-4deb88-1608214888461)]

  8. 打開識(shí)別(Identity)檢查器。

  9. 在識(shí)別檢查器中,修改 Class 區(qū)域的 ViewController 為 MealViewController。

    [圖片上傳失敗...(image-c80217-1608214888461)]

檢驗(yàn):編譯運(yùn)行你的 app。一切都和之前一樣。

你或許看到一個(gè) Xcode 警告說明無法訪問 app 中的食物場景。不要擔(dān)心,會(huì)在下一課添加導(dǎo)航行為。

注意:

為了看到本課的完整示例項(xiàng)目,下載文件并在 Xcode 中查看它。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容