马上着手开发 iOS 应用程序 (八) - 创建表格视图

重要:这是针对于正在开发中的API或技术的预备文档(预发布版本)。苹果提供这份文档的目的是帮助你按照文中描述的方式对技术的选择及界面的设计开发进行规划。这些信息有可能发生变化,因此根据本文档的软件开发应当基于最终版本的操作系统和文档进行测试。该文档的新版本或许会随着API或相关技术未来的发展而进行更新。

翻译自苹果官网:

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

在本课中,创建第二个场景,它基于 table view(表格视图) 并列出用户的所有食物。将会创建自定义表格单元格来显示每一份食物,最后它应该像这样:

[图片上传失败...(image-a0b7ee-1608214888461)]

学习目标

在课程的最后,你将能够:

  • 创建第二个 storyboard 场景
  • 理解 table view 的关键部分。
  • 创建和设计一个自定义 table view cell(表格视图单元格)。
  • 理解 table view 的代理和数据源。
  • 使用一个数组来存放和使用数据。
  • 在 table view 中显示动态数据。

创建开始场景

目前,FoddTracker app 有单个被视图控制器管理的食物场景,用户可以在其中添加和评价食物。现在是时候创建新的场景来显示所有食物的列表。幸运的是,iOS 有个非常强大名叫 table view(UITableView) 的内置类,它被特别设计用来显示一个滚动列表。

table view 被 UITableViewController 管理,它是 UIViewController 的子类专门用来处理 table view 相关的逻辑。你将会创建一个新的基于它的场景。

添加一个带 table view 的场景到 storyboard 中
  1. 打开 Main.storyboard。
  2. 在实用工具区中打开对象库。(或者,选择 View > Utilities > Show Object Library。)
  3. 在对象库中,找到 Table View Controller 对象。
  4. 拖动 Table View Controller 放在画板中食物场景的左边。
    确保拖动 table view controller 而不是 table view 对象。table view 是被 table view controller 管理的对象,但是需要的是控制器而不是单个 table view,所以找到 table view controller 并拖动它到画板中。

现在有两个场景了,一个用于显示食物列表,一个是用于添加新的食物。

让食物列表成为用户启动你的 app 后第一个看到的场景是很有意义的,所以设置 table view controller 作为首要场景告诉 Xcode 你的意图。

设置 table view controller 作为启动场景
  1. 如果想要更多空间来工作,点击 Xcode 工具栏中的 Navigator 和 Utilities 按钮来收缩项目导航和实用工具区。

    [图片上传失败...(image-dfbe7d-1608214888461)]

  2. 从食物场景中拖动 storyboard entry point 到 table view controller 中。

    [图片上传失败...(image-4209ae-1608214888461)]

    在你的 storyboard 中设置 table view controller 作为启动视图控制器,这让它在 app 启动的时候成为第一个加载的场景。

    [图片上传失败...(image-2750a3-1608214888461)]

检验:运行你的 app。取代之前那个拥有文本框,image view 和评分控件的食物场景,你应该看到一个空的 table view - 它被水平分割线分成一行行但是各行并没有内容。

[图片上传失败...(image-402f0d-1608214888461)]

你需要修改 table view 的设置这样才能在你的 app 中使用。

配置 table view
  1. 在你的 storyboad 中,打开大纲视图。

  2. 在大纲视图中,选择 Table View。

    table view 嵌套在 Table View Controller Scene > Table View Controller 的下面。你或许要点击旁边的三角形才能看到嵌套的 table view。

    [图片上传失败...(image-be797c-1608214888461)]

  3. 选中 table view,在实用工具区打开尺寸检查器。

    选中检查器选择栏左边第五个按钮打开尺寸检查器。它能让你编辑 storyboard 中对象的尺寸和位置。

    [图片上传失败...(image-503b71-1608214888461)]

  4. 在尺寸检查器中,找到名为 Row Heigh 的区域输入 90。回车确认。

继续设计单元格界面,稍后将会回来使用这个 table view。

设计自定义 table view 单元格

table view 中单独的一行是由 UITableViewCell 管理的。单元格有不同的预定义的功能以及一些默认的样式。默认的单元格样式在很多情况下都很棒,但是在本课中需要在单元格中显示更多内容,而这超过了默认样式所允许的了,所以需要自定义一个单元格样式。

创建 UITableViewCell 的子类
  1. 选择 File > New > File(或者按 Command-N)。
  2. 在出现的对话框左边,选择 iOS 下面的 Source。
  3. 选择 Cocoa Touch Class,然后点击 Next。
  4. 在 Class 区域,输入 Meal。
  5. Subclass Of 区域,选择 UITableViewCell。
    类的标题自动修改为 MealTableViewCell。从命名很清楚的知道你在创建一个自定义 table view 单元格,所以让它成为新的名字。
  6. 确保语言选项设置为 Swift 。
  7. 点击 Next。
    默认保存位置是你的项目目录。
    Group 选项默认是你的 app 名字,FoodTracker。
    在 Targets 区域,确保你的 app 选中而 targets 不选中。
  8. 让剩下的使用默认选项,然后点击 Create。
    Xcode 创建了 MealTableViewCell.swift 文件并定义了 MealTableViewCell 类。

现在,打开 storyboard。
你会注意到 storyboard 中 table view 只显示了单个单元格。

[图片上传失败...(image-9063d7-1608214888461)]

这个单元格代表其他单元格的原型;其中定义的设计和功能会被 table view 的其他单元格使用。但是首先,需要做一点配置。连接场景中的单元格和刚才创建的自定义单元格子类。

为 table view 创建一个自定义单元格
  1. 在大纲视图中,选择 Table View Cell。
    单元格嵌套在 Table View Controller Scene > Table View Controller > Table View 下面。你或许需要展开这些对象才能看到单元格。

    [图片上传失败...(image-e1f748-1608214888461)]

  2. 选中单元格,在实用工具区打开属性检查器。

  3. 在属性检查器中,找到叫 Identifier 的区域输入 MealTableViewCell。回车确认。
    这是很重要的一步 - 之后你就知道为什么了。

  4. 在属性检查器中,找到名为 Selection 的区域选择 None。
    使用这个选项,当用户点击单元格的时候不会产生视觉高亮的效果。

  5. 打开尺寸检查器。

  6. 在尺寸检查器中,找到名为 Row Height 的区域输入 90。
    确保旁边的 Custom 复选框选中了:

    [图片上传失败...(image-3262a1-1608214888461)]

    回车确认在 storyboard 中显示了新的单元格高度。

  7. 打开识别(Identity)检查器。
    回忆下识别检查器让你在 storyboard 中编辑一个对象的 Identity(标识) 相关的属性,例如对象属于哪个类。

  8. 在识别检查器中,找到名为 Class 的区域并选择 MealTableViewCell。

    [图片上传失败...(image-b5eaeb-1608214888461)]

配置完单元格,可以在 storyboard 中设计它的自定义界面。它包含食物的名字,照片和评分,应该像这样:

[图片上传失败...(image-446087-1608214888461)]

为了达到这个效果,需要一个标签,一个 image view 和一个评分控件。你可以复用在前一课中创建的评分控件。

设计自定义单元格的界面
  1. 选择 Editor > Canvas > Show Bounds Rectangles 来显示界面控件的边界,让控件在单元格中更容易对齐。

    [图片上传失败...(image-e2c849-1608214888461)]

  2. 使用对象库寻找一个 Image View 对象并拖动到单元格中。

  3. 拖动和调整 image view 的尺寸让它平行并填充单元格的左边、顶部和底部。

    [图片上传失败...(image-a3aff2-1608214888461)]

  4. 如果你在前一课没有添加默认的图片到你的项目中,现在添加吧。

  5. 选中这个 image view,在实用工具区打开属性检查器。

  6. 在属性检查器中,找到名为 Image 的区域并选择 defaultPhoto。

    [图片上传失败...(image-114f6b-1608214888461)]

  7. 使用对象库来找到一个标签对象并拖动到单元格中。

  8. 拖动标签让它接近 image view 的右边并对齐表格单元格的顶部边界。

    [图片上传失败...(image-6317a8-1608214888461)]

  9. 调整标签的尺寸让它的右边伸展到单元格的右边缘。

    [图片上传失败...(image-614a94-1608214888461)]

  10. 使用对象库找到一个 View 对象并拖动到单元格中。

  11. 选中这个视图,在实用工具区打开尺寸检查器。

  12. 在尺寸检查器的 Height 区域输入 44,Width 区域输入 240。回车确认。

  13. 拖动视图让它在标签的下面并对齐标签的左边缘。

    [图片上传失败...(image-a4711e-1608214888461)]

  14. 选中视图,打开识别检查器。

  15. 在识别检查器中,找到名为 Class 的区域选择 RatingControl。

    [图片上传失败...(image-c7b36b-1608214888461)]

    如果你没有看到 RatingControl 作为弹出菜单的选项,确保你在画板(在前一张图片中显示了 resize handles 的那个)中选择了正确的界面控件。

  16. 选中视图,打开属性检查器。

  17. 在属性检查器中,找到名为 Interaction 的区域并取消选中 User Interaction Enabled 复选框。
    之前设计的自定义评分控件类主要用于交互,但是在单元格中是不需要交互。所以当它在这个环境中关闭用户交互,这是很重要的。

我们的界面应该像这样:

[图片上传失败...(image-5c5a6e-1608214888461)]

检验:运行你的 app。单元格现在看起来更高了。但是即使你向单元格中添加了所有必须的界面控件,table view 还是像以前一样是空的,为什么会那样?

[图片上传失败...(image-23e779-1608214888461)]

在一个 storyboard 中,table view 可以配置用来显示静态的数据(storyboard 支持)或者动态数据(table view controller 逻辑支持)。table view 默认使用动态数据,所以你需要在代码中加载数据,这正是你希望做的-你仅仅需要实现这个行为。这意味着你在 storyboard 中提供的静态内容并不会在运行时显示,所以看不到 table view 的内容 - 直到你实现了它之后的数据模型。

现在,使用辅助编辑器预览界面。

预览界面
  1. 点击 Xcode 工具栏中的 Assistant 按钮来打开辅助编辑器。

    [图片上传失败...(image-c51733-1608214888461)]

  2. 如果想要更多空间来工作,通过点击 Xcode 工具栏中的 Navigator 和 Utilities 按钮来收缩项目导航和实用工具区。

    [图片上传失败...(image-597a84-1608214888461)]

    同样可以收缩大纲视图。

  3. 在出现的辅助编辑器顶部的编辑器选择栏中,把辅助编辑器从 Automatic 切换为 Preview > Main.storyboard(Preview)。

    [图片上传失败...(image-faabb2-1608214888461)]

    你的 Xcode 窗口应该像这样:

    [图片上传失败...(image-591f52-1608214888461)]

预览界面看起来像预期的那样。原型单元格界面看起来完成了。

注意

如果你在界面预览中看到错误的场景了,通过点击场景 dock 确保选择了 table view 场景。

添加图片到项目中

下一步,往你的项目中添加示例图片。当加载初始食物数据到你的 app 时会用到这些图片。

你可以在课程最后项目的 Images/ 文件夹中找到示例图片,或者使用你自己的图片。(确保使用的图片的名字匹配稍后的代码中的图片名字。)

添加图片到项目中
  1. 如果辅助编辑器打开了,点击 Standard 按钮返回标准编辑器。
    通过点击 Xcode 工具栏中的 Navigator 和 Utilities 按钮打开项目导航和实用工具区。

    [图片上传失败...(image-dee0d3-1608214888461)]

  2. 在项目导航中,选择 Assets.xcassets 来查看 asset catalog。
    回忆下 asset catalog 是 app 中储存和组织图片资源的地方。

  3. 点击左下角的加号(+)从弹出菜单中选择 New Folder。

  4. 双击文件夹名字将它重命名为 Sample Images。

  5. 选中文件夹,点击左下角的加号(+)按钮在弹出的菜单中选择 New Image Set。

  6. 双击图片集合名字把它重命名为写代码时容易记住的名字。

  7. 选择你电脑中想要添加的图片。

  8. 拖动图片并把它放到图片集合的 2x 槽中。

重复 5-8 操作来尽可能添加你喜欢的图片。剩下的课程假定你已经有三张不同的图片了。

[图片上传失败...(image-df5a8-1608214888461)]

连接单元格界面和代码

在单元格显示动态数据之前,你需要在视图和单元格的代码之间创建 outlet 连接。

连接视图和 MealTableViewCell.swift 代码
  1. 在你的 storyboard 中,选择 table view cell 中的标签。

  2. 点击 Xcode 工具栏中的 Assistant 按钮来打开辅助编辑器。

    [图片上传失败...(image-828ac7-1608214888461)]

  3. 如果想要更多空间来工作,通过点击 Xcode 工具栏中的 Navigator 和 Utilities 按钮来收缩项目导航和实用工具区。

    [图片上传失败...(image-497fb-1608214888461)]

  4. 在辅助编辑器顶部的编辑器选择栏中,将辅助编辑器从 Preview 切换到 Automatic > MealTableViewCell.swift。

    [图片上传失败...(image-a52c35-1608214888461)]

    MealTableViewCell.swift 显示在右边编辑器中。

  5. 在 MealTableViewCell.swift 中,找到 class 行,应该像这样:

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

     // MARK: Properties
    
  7. 按住 Control 从画板中的标签拖动到右边编辑器的代码中,在 MealTableViewCell.swift 中刚添加的注释的下面停止拖动。

    [图片上传失败...(image-23805e-1608214888461)]

  8. 在出现的对话框中输入名字:nameLabel。
    忽略剩下的选项。你的对话框应该像这样:

    [图片上传失败...(image-303f69-1608214888461)]

  9. 点击 Connect。

  10. 在你的 storyboard 中,选择 table view cell 中的 image view。

  11. 按住 Control 从画板的 image view 拖动到右边编辑器的代码中,在 MealTableViewCell.swift 的属性 nameLabel 下面一行停止拖动。

    [图片上传失败...(image-232790-1608214888461)]

  12. 在出现的对话框中,输入名字: photoImageView。
    忽略剩下的选项点击 Connect。
    [图片上传失败...(image-b268a4-1608214888461)]

  13. 在你的 storyboard 中,选择 table view cell 中的评分控件。

  14. 按住 Control 从画板的评分控件拖动到显示在右边编辑器的代码中,在 MealTableViewCell.swift 的 photoImageView 属性的下面一行停止拖动。

    [图片上传失败...(image-b9541c-1608214888461)]

  15. 在出现的对话框中,输入名字:ratingControl。
    忽略剩下的选项点击 Connect。

    [图片上传失败...(image-43bee1-1608214888461)]

MealTableViewCell.swift 中 outlets 应该像这样:

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

加载初始数据

为了在你的单元格中显示一些真实数据,你需要编写代码来加载数据。此刻,你有个食物的数据模型:Meal 类。你同样需要保存这些食物的列表。很自然在自定义视图控制器的子类中保存这些数据。视图控制器会管理视图来显示食物列表,后面会保存一个数据模型的引用。

首先,创建一个自定义控制器子类来管理食物列表场景。

创建 UITableViewController 的子类
  1. 选择 File > New > File(或者按 Command-N)。

  2. 在出现的对话框左边,选择 iOS 下面的 Source,之后选择 Cocoa Touch Class。

  3. 点击 Next。

  4. 在 Class 区域,输入 Meal。

  5. 在 Subclass of 区域,选择 UITableViewController。
    类的标题相应修改为 MealTableViewController。就让它这样。

  6. 确保 Also Create XIB file 选项没有选中。

  7. 确保语言选项设置成了 Swift。

  8. 点击 Next。

    默认的保存位置是你的项目目录。
    Group 选项默认是你的 app 名字,FoodTracker。
    在 Target 区域,你的 app 被选中而 tests 未选中。

  9. 剩下都是默认值,然后点击 Create。

    Xcode 创建 MealTableViewController.swift,一个自定义 table view controller 子类的源代码文件。

在自定义子类中,定义一个属性来存储 Meal 对象的列表。Swift 标准库包含一个叫 Array 的数据结构非常适合记录这种列表。

加载初始数据
  1. 如果辅助编辑器打开了,点击 Standard 按钮返回标准编辑器。

    点击 Xcode 工具栏中的 Navigator 和 Utilities 按钮展开项目导航和实用工具区。

    [图片上传失败...(image-ce4aa9-1608214888461)]

  2. 打开 MealTableViewController.swift。

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

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

    代码定义了 MealTableViewController 的一个属性并将它初始化默认的值(一个空的食物对象数组)。通过设置 meals 为一个变量而不是常量,让数组可变化,这意味着你可以在初始化它之后添加对象。

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

     func loadSampleMeals() {
     }
    

    这是一个帮助方法用来加载 app 的示例数据。

  5. 在 loadSampleMeals() 方法里面,添加代码来创建一些 Meal 对象。你可以凭着自己喜欢来给这些示例食物命名和设置评分,当然,这里是一些例子:

     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)!
    

确保项目中图片名字匹配你在代码中写的名字。

  1. 在创建完食物对象后,使用如下代码添加到 meals 数组中。

     meals += [meal1, meal2, meal3]
    
  2. 找到 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()
     }
    

    当创建 MealTableViewController.swift 时候 Xcode 默认插入一些注释。本课中不需要它们。

  3. 在 viewDidLoad() 方法中,删除注释并替换为这些代码用来在 super.viewDidLoad() 后面来加载示例食物数据:

     // Load the sample data.
     loadSampleMeals()
    

    调用刚才添加的帮助方法用来当视图加载的时候加载数据。你分割功能到定义的方法来让代码更模块化和可读。

    viewDidLoad() 方法应该像这样:

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

    loadSampleMeals() 方法应该像这样:

     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]
     }
    

检验:通过选择 Product > Build 来运行你的项目。编译应该没有错误。

重要:

如果运行出现编译问题,确保项目中的图片名字确实匹配代码中的名字。

显示数据

此刻,你的自定义控制器子类, MealTableViewController,有个可变的数组包含一些示例食物。现在你需要在界面上显示这些真实数据。

为了显示动态数据,table view 需要两个很重要的帮手:数据源和代理。数据源,就像名字说的,提供给 table view 需要显示的数据。代理帮助 table view 管理单元格选中、行高度和其他显示数据相关功能。UITableViewController 和它的子类默认遵循了必要的协议来让它既是一个数据源(UITableViewDataSource 协议)又是一个代理(UITableViewDelegate)。你的工作就是在 table view controller 中实现合适的协议方法这样 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 有多少个区域用来显示。Sections 是 table views 中单元格视觉分组,尤其在 table views 有很多数据时候有用。一个简单的 table view 例如我们 app 的,仅仅需要显示单个组,所以 numberOfSectionsInTableView(_:) 数据源方法的实现很简单。

在 table view 中显示一个组
  1. 在 MealTableViewController.swift 中,找到 numberOfSectionsInTableView(_:) 数据源方法。模板默认实现如下:

     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 个区域而不是0个。移除 #warning Incomplete implementation 注释因为你已经完成了实现。

下一个数据源方法, tableView(_:numberOfRowsInSection:) 告诉 table view 在一个给定组中显示多少行。每份食物应该在组中有属于它自己的行,这意味着行的数量应该等于 meals 数组中 Meal 对象的数量。

返回 table view 中行的数量
  1. 在 MealTableViewController.swift 中,找到 tableView(_:numberOfRowsInSection:) 数据源方法。模板默认实现如下:

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

    返回你拥有的食物数量。Array 有个属性名叫 count 并且返回 array 中对象的数量,所以行的数量是 meals.count。

  2. 修改 tableView(_:numberOfRowsInSection:) 数据源方法来返回合适行的数量,移除警告注释。

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

最后的数据源方法,tableView(_:cellForRowAtIndexPath:),配置单元格来显示给定的行数据。table view 的每一行对应一个单元格,那个单元格决定行中显示的内容以及怎么布局。

当 table views 有很少行的时候,所有的行或许同时都显示在屏幕上,所以为 table view 的每一行调用这个方法。但是当 table views 有很多行,同时只能显示所有中的一小部分。只为正在显示的行请求数据是很有效率的,那正是 tableView(_:cellForRowAtIndexPath:) 允许 table view 做的。

为任何 table view 中给定的行,通过取 Meals 数组中对应的 Meal 对象来配置单元格,使用 Meal 对象的属性值设置单元格中对应的控件。

在 table view 中配置和显示单元格
  1. 在 MealTableViewController.swift 中,找到并取消 tableView(_:cellForRowAtIndexPath:) 数据源方法的注释。(为了取消方法的注释,移除环绕它的 /* 和 */ 字符。)
    当你做了这些,模板默认实现如下:

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

方法执行一些操作。首先通过占位符标识向 table view 请求一个单元格,添加一个注释表示在这里配置单元格,之后返回单元格。

为了让代码工作,修改占位符标识为之前你在原型单元格中设置的(MealTableViewCell),之后添加代码来配置单元格。

  1. 在方法的开始添加代码:

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

    为 storyboard 中设置的标识创建了一个常量。

  2. 更新占位符标识为 storyboard 中设置的标识。方法第二行代码应该像这样:

     let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)
    
  3. 因为使用了自定义的单元格类,强转单元格类型为你自定义单元格子类,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 数组中拿到合适的食物。

  5. 删除 // Configure the cell 注释并在这个地方添加代码:

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

    使用 meal 中对应数据来设置 table view 单元格的每个视图。

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
    }

最后一步就是连接 MealTableViewController.swift 中定义的代码和 storyboard 的场景。

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

  2. 点击场景 dock 选择 table view 控制器,可以看到它周围有个蓝色的框。

  3. 打开识别检查器。

  4. 在识别检查器中,找到名为 Class 的区域,选择 MealTableViewController。

    [图片上传失败...(image-4a89fa-1608214888461)]

检验:运行 app。在 viewDidLoad() 方法中添加的列表项应该显示在 table view 中作为单元格。你会注意到 table view 单元格和状态栏之间有一点重叠 - 在下一课会修复它的。

[图片上传失败...(image-85ded9-1608214888461)]

准备食物场景用于导航

在 FoddTracker app 中实现导航功能前,需要删除一些不再需要的代码和控件。

清理不需要的代码和控件
  1. 打开 storyboard 查看食物场景。

    你的食物场景界面应该像这样:

    [图片上传失败...(image-429f7c-1608214888461)]

  2. 在食物场景中,选择 Meal Name 标签,按 Delete 键删除它。

    堆栈视图会合理调整剩余的控件位置。

    [图片上传失败...(image-3883f7-1608214888461)]

  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!
    

因为现在项目中两个视图控制器了,给 ViewController.swift 一个更有意义的名字吧。

重命名 ViewController.swift 文件
  1. 在项目导航中,点击 ViewController.swift 文件并按回车键。
    Xcode 让你为文件输入新的名字。

  2. 重命名文件为 MealViewController.swift,回车确认。

  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. 点击场景 dock 选中食物场景。

    [图片上传失败...(image-4deb88-1608214888461)]

  8. 打开识别(Identity)检查器。

  9. 在识别检查器中,修改 Class 区域的 ViewController 为 MealViewController。

    [图片上传失败...(image-c80217-1608214888461)]

检验:编译运行你的 app。一切都和之前一样。

你或许看到一个 Xcode 警告说明无法访问 app 中的食物场景。不要担心,会在下一课添加导航行为。

注意:

为了看到本课的完整示例项目,下载文件并在 Xcode 中查看它。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容