IOS Apps 开发(Swift)(8)——Create a Table View

前言:网上一直没有找到用Swift开发IOS的好的教程,所以找了官网的文档翻译一下算了。如有错误欢迎指正。博主首发CSDN,mcf171专栏。

博客链接:mcf171的博客

原文链接:Create a Table View

——————————————————————————————

在本次课程中,我们将创建FoodTracker app的主屏幕。我们将创建第二个基于 table view 的场景,从而显示用户的菜品


学习目标

在本次课程中,你可以了解到:

  • 创建第二个storyboard场景
  • 理解table view 的关键概念
  • 创建和设计自定义的table view 单元
  • 理解table view 代理的角色和数据源
  • 理解数组如何存储和处理
  • 动态的展示table view 的数据

创建开放的场景

目前为止,我们的FoodTracker app 有了一个 场景了,并且通过 view controller管理。也就是菜品场景,在这个场景中用户可以增加一个新的菜品并且打分。现在我们需要创建一个场景来展示整个菜品列表。在iOS中有一个强有力的类可以支持我们的想法,那就是Table view(UITableView)


table view 同样也有一个控制器管理(UITableViewController),这个类是UIViewController的子类。我们将创建一个基于table view 控制器的新场景。

给我们的storyboard增加一个新的场景

1、打开Main.storyboard

2、打开实用工作区的对象库。(View > Utilities > Show Object Library.)

3、在对象库中找到Table View Controller 对象

4、将Table View Controller 拖拽到画布菜品场景的左边上

当你从对象库拖拽到画布上的是偶,如果看到了一个table view 但是什么都没发生,可能是因为你拖得是一个 table view 而不是 table view controller。


现在我们有了两个场景,一个是显示菜品列表的,一个是增加新的菜品。

讲道理来说显示菜品列表应该是我们加载app第一个看到的页面,所以在Xcode中设置,从而让菜品列表称为第一个场景。

设置table view 作为第一个初始化场景

1、如果想要更多的工作空间,折叠两个


2、将 storyboard entry point 从菜品场景拖到table view controller


经过上述操作,table view controller就变成了初始化的场景。


里程碑:运行app。我们看到的现在是一个空的 table view


接下来我们需要做一些设置上的改动从而使我们可以操作 table view

配置 table view

1、在storyboard中打开大纲视图

2、在大纲视图选择 Table View


3、选中Table View 之后,打开实用工作区的尺寸查看器


4、将Row height 设置为90

接下来我们需要设置Table View 里面的元素

自定义Table Cells

在Table view 中的每一行都是受table view cells(UITableViewCell)管理。这些单元有很多预定义的行为和默认的风格。默认的单元风格在很多情况下都非常适合,但是因为在每个单元中我们有更多的内容需要来展示,所以我们需要自定义单元风格。

创建UITableViewCell的子类

1、 File > New > File (or press Command-N).

2、在弹出框选择iOS 中的Source

3、选择 Cocoa Touch 类,点击下一步

4、在Class 中填写 Meal

5、在 Subclass of 中选择UITableViewCell

类名将变为MealTableViewCell。Xcode将自动帮我们进行命名。

6、确认语言是Swift

7、下一步

8、点击Create

Xcode将创建MealTableViewCell.swift文件


现在打开storyboard

我们发现在table view中只有一个单元


这个单元是其他单元的模型。我们针对这一个单元的设计都会用到table view的其他单元上。但是我们需要进行一些配置,将table view cell 和我们自定义的单元子类联系起来

为table view 配置自定义单元

1、在大纲视图,我们选择Table View Cell


2、确保选中单元的同时打开属性查看器。

3、在属性查看器中找到 Identifier 输入 MealTableViewCell

4、在属性查看器中找到 Selection 选择Node

上述设置是使得当单元被点击的时候不会有可视化的高亮

5、打开尺寸查看器

6、设置Row Height为90

确保 Custom 前的被勾上了


7、打开 身份查看器

8、在身份查看器的Class中选择MealTableViewCell


有了上述的设置,我们就可以自定义一个UI了效果如下:


现在我们需要一个标签,一个Image view 和一个打分控制

设计自定义Table 单元

1、Editor > Canvas > Show Bounds Rectangles 来展现UI中元素的边界


2、从对象库中拖一个 Image View 对象到Table 单元中

3、调成 image view的位置,使它看起来如下


4、如果在之前的课程没有添加默认图片,现在添加一张

5、选中 image view,打开属性查看器

6、在属性查看器中找到 Image 选择 defaultPhoto


7、从对象库中拖拽一个标签对象

8、调整标签的位置


9、重新调整label的大小


10、从对象库中找到一个 View 对象,拖到table 单元中

11、选中View 打开尺寸查看器

12、设置高44,宽240。回车

13、将view 拖到如图所示位置


14、选中view ,打开身份查看器

15、选择Class为RatingControl


如果没有看到RatingControl的选项,请确认你是否选择的是正确的UI元素

16、选中view 打开属性查看器

17 找到 Interaction 选项,取消 User Interaction Enabled 前的勾

之前设计的 RatingControl 是用来交互的,但是目前这个是不需要交互的,只用展示就好了。

现在UI看起来应该如图所示


里程碑:运行app,这个Table view 单元看起来应该更高了。虽然我们已经将所有的UI元素都加入了,但是现在什么都没有显示,这是为什么呢?


在Storyboard中,table view 既可以显示静态数据,也可以显示动态数据。但是默认情况下是显示动态数据,但是我们还没有实现,所以就没有数据。所以也就意味着在运行时我们在storyboard上做的静态数据是显示不了的。所以我们什么都没看见。

从现在开始我们使用助手编辑器预览我们的UI

预览我们的接口

1、点击助手按钮来打开助手编辑器


2、如果需要更多的工作空间,折叠左右两个工作区



3、在编辑器选择去切换到 Preview》Main.storyboard


现在你的Xcode看起来应该是这样


这个预览图显示我们的UI如我们预期的那样进行了显示。

添加图片到我们的工程中

接下来我们需要添加一些样例图片到工程中。

你可以在本次课程最后的链接中下载工程,文件资源在Images文件夹下

添加图片到工程中

1、返回标准编辑器,展开左右两个工作区


2、点击Assets.xcasset来查看资源目录

3、在左下角点击+号,添加新的文件夹

4、双击文件夹重命名为Sample Images

5、选中文件夹,新建图像集

6、双击图像集,重命名为mealx(随便自己写)

7、在电脑上选择你想拖拽的图片

8、拖到2x的框中

你想添加几张就重复5-8步几次,在接下来的课程都假设你已经添加了三张不同的图片


将Table Cell UI 和代码联系起来

在我们把数据动态的在table view上显示之前,我们需要在storyboard和代码之间创建一个outlet。

连接视图和MealTAbleVIewCell.swift

1、打开storyboard。选择在table view 单元中的 标签

2、打开助手编辑器


3、如果想要更大的工作空间,折叠两边的工作区


4、在编辑选择器选择Automatic》MealTableViewCell.swift


5、找到MealTableViewCell.swift的类声明的行数

class MealTableViewCell: UITableViewCell {

6、添加以下代码

// MARK: Properties

7、使用control + 拖拽的方式将标签从画布拖到编辑器代码代码的下方


8、在对话框中的名字输入:nameLabel


9、点击Connect

10、在storyboard中选择Image View

11、使用Control + 拖拽的方式将 image view从画布拖到代码中的nameLabel下


12、在对话框中输入名字:photoImageView


13、在storyboard中选择分数控制

14、使用Control + 拖拽的方式把分数控制从画布拖到编辑器里面


15、在对话框中名字为:ratingControl


现在在MealTableViewCell.swift 中的outlets为

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

加载初始化数据

为了在table 单元中显示任何实时数据,我们需要写一些必要的代码来加载数据。首先,我们已经有了数据模型Meal 类,同时我们也需要去保存菜品的列表。很自然的想到我们可以在自定义的view controller 的子类中存放这些数据。

首先创建一个自定义table view controller的子类来管理菜品列表场景

1、  File > New > File (or press Command-N).

2、在选择iOS下的Source 点击Cocoa Touch 类

3、点击下一步

4、Class名输入 Meal

5、在"SubClass of" 中选择UITableViewController

类名自动变为MealTableViewController

6、不要勾选"Also create XIB 文件"

7、确定语言为Swift

8、点击下一步

9、点击Create

Xcode将创建一个MealTableVIewController.swift

在自定义的子类中中,我们现在可以定义存储菜品列表的属性,我们可以使用Array来存储

加载初始化数据

1、如果在助手编辑器中,返回标准编辑器


2、打开MealTableViewController.swift

3、在类名下加入以下代码

// MARK: Properties
 
var meals = [Meal]()
上述代码声明了一个空的数组。
4、在vieDidLoad()方法后添加一个方法

func loadSampleMeals() {
}

5、在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)!

6、在创建了meal对象之后,我们保存倒数组中

meals += [meal1, meal2, meal3]

7、找到vieDidLoad()方法,模板的实现如下:

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、删除上述注释,在super.viewDidLoad方法后面添加如下代码

// Load the sample data.
loadSampleMeals()

最终  vieDidLoad 方法为

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,应该没有任何错误

显示数据

到这个时候我们自定义了table view controller 的子类 MealTableViewController。同时有了数组存放样例数据。现在我们需要做的是在UI上显示数据。

为了实现动态的显示数据,table view 需要两个重要的帮手,一个数据源和一个代理。数据源能提供table veiw 需要显示的数据,代理帮助table view 管理单元的选择、行高和其他展示数据相关的方面。默认情况下UITableViewController和他的子类采用一些必须的协议来使得table view和数据源(UITableViewDataSource接口)和代理(UITAbleViewDelegate)进行关联。我们的工作只是实现这些接口中一些方法就可以了。

一个完备的Table view 需要三个数据源方法。

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

第一个方法是告诉table view  需要展示多少个段。段是一个可视化的单元组。比如说我们现在的table view,我们需要去展示一个简单的段。所以我们需要实现第一个方法

在table view中显示一个段

1、打开MealTableViewController.swift,找到numberOfSectionInTableView数据源方法。模板是

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
}
上述代码让tableView显示1个段。

在下一个数据源方法tableView,告诉了table view 在一个给定的段中有多少行来显示。一个table view 默认有一个单一的段,这也是我们app需要的。每个菜品需要在段中有自己的行。也就意味着行数应该等于菜品对象的个数

返回table view 的行数

1、找到tableView数据源方法,实现模板为

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

我们需要返回我们的菜品个数,数组有一个属性count可以返回在数组中的元素个数。所以我们直接返回meals.count就可以了

2、改变上述方法代码,并移除注释

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


(下一段博主没太懂)
最后一个数据源方法为每一行配置和提供了一个单元。table view中的每一行都有一个单元。这个单元决定了在行中出现的内容也内容如何布局。

对于一小部分的行,所有的行可以一次性出现在屏幕中,所以这个方法会被每个行调用。但是当有很多行出现在table view的使用,我们只能一次显示一部分行。对于table view 来说最有效的是请求单元显示的行数是多少。

对于任何在table view中的行,我们需要通过延迟在数组中相应的菜品来配置单元,

配置和显示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
}

上述模板执行了很多任务,首先用一个占位符标识来请求一个单元,同时用注释说明了需要来配置单元。

为了满足我们的app我们需要将占位符标识该厂我们自己之前定义的(MealTableViewCell)然后配置单元

2、在方法最前面增加代码

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

3、更新占位符标识

let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath)

4、因为我们创建了自定义的单元类,因此我们需要向下转型为我们自定义的子类

et cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! MealTableViewCell

5、在上一行再添加以下代码

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

6、删除//Configure the cell 注释,增加以下代码

cell.nameLabel.text = meal.name
cell.photoImageView.image = meal.photo
cell.ratingControl.rating = meal.rating
现在 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文件中的菜品列表相关联

将table view controller 指向 MealTableController.swift

1、打开storyboard

2、点击场景dock来选择table view controller


3、打开身份查看器

4、找到class 选项,选择MealTableViewController


里程碑:运行app,在viewDidLoad中添加的菜品现在讲全部显示出来,同时我们注意到单元和最上面的状态栏有一点点重叠,我们将在下一次课程进行修复


为菜品场景的导航做准备

我们现在准备实现FoodTracker app的导航功能。我们需要删除和替换UI中的一些地方

整理项目中没用的地方

1、打开storyboard,查看菜品场景

2、选择Meal Name 标签,然后删除它


3、打开ViewController.swift

4、找到textFieldDidEndEditing方法

func textFieldDidEndEditing(textField: UITextField) {
    mealNameLabel.text = textField.text
}

5、删除一下代码

mealNameLabel.text = textField.text

6、删除mealNameLabel 的outlet,同时删除其他存在mealNameLabel的地方

@IBOutlet weak var mealNameLabel: UILabel!

因为我们现在有两个view controller了,所以我们需要改一下原有的名字使它更有意义

重命名

1、点击ViewController.swift,然后回车

2、重命名为MealViewController.swift

3、找到类声明的地方

class ViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

4、修改了类名为MealViewController

class MealViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

5、在文件最上方的注释也修改类名

6、打开storyboard

7、点击场景dock选中菜品场景


8、打开菜品场景的身份查看器

9、将Class选项的类名修改为MealViewController


10、编译并且运行app。没有报错

到了这一步,我们会发现Xcode有一个警告。不用担心之后的课程我们会修复这个警告

ps:本课程完整的工程Download File




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值