第六章 :使用Prototype Cell 自定义表视图

第六章 :使用Prototype Cell 自定义表视图

译者注:由于本人英语水平有限,尽可能描述出作者的本意。如有错误,及时指出。文中会省略部分技术无关的赘述

It’s very easy to be different, but very difficult to be better.
                                                            – Jonathan Ive

这里写图片描述


使用Prototype Cell自定义表试图

在上一章节我们创建了一个简单的基于表视图的app用来展示餐厅信息 。在这章,我们将会自定义tableview cell 使他看起来更时尚。我们将一起对它做一些更改和加强:

  • 重建使用UITableViewController构建一个相同的app

  • 每行展示不同的图片代替所有行显示相同图片

  • 设计一个自定义tableview cell 而不是仅仅使用基础样式的tableview cell

你可能疑惑为啥要重建一个一样的项目 。 有很多方式去做这个事情 。此前我们从对象库中拖一个UITableView去创建表视图 ,一会我们将会使用UITableViewController去创建 。它会更简单吗 ?yes , 它比上一个更简单 。回想一下,前面的例子我们必须实现UITableViewDataSource 和 UITableViewDelegate 协议 。UITableViewController自动帮我们实现了协议而且设置了连接(代理). 除此之外,它包括所有上一节的自动布局约束。


使用UITableViewController创建表视图app

首先 ,我们使用UITableViewController重新创建相同的app。创建一个”Single View application” , 项目名称 FooPin(我们创建的是一个真正的app ,所以起一个好点得名字)像前面章节那样填好别的选项。

这里写图片描述

创建好项目后,选择”Main.storyboard”进入Interface Builder 。像前面一样取消勾选Size Classes 。 因为我们选择的是”Single View application” , 系统默认会生成一个View Controller 。我们这里不用系统默认的 。选择view Controller 点击delete按钮删除它 。它关联的ViewController.swift 也删除掉 。

回到storyboard 。从对象库拖一个Table View Controller(或者UITableViewController) 出来放在storyboard 。把它设置成initial View Controller ,就是第一次加载的控制器 。你所要做得就是在属性编辑器中勾选”Is Initial View Controller”选项 。你会看到一个箭头指向这个控制器 。如图 。

这里写图片描述

如果你编译运行,你会得到一个空得tableveiw 。明显我们没有增添任何数据 。
目前,这个表试图控制器跟基本的UITableViewController关联的 。为了方便处理数据,我们需要提供自己的类与之关联 。

这里写图片描述

如图 ,创建一个新的“Cocoa Touch Class” 起名为“RestaurantTableViewController” 。并让它继承自UITableViewController 。 把”Subclass of” 改成”UITableViewController” 。保持其他值不变 。如下图

这里写图片描述


父类和子类

Swift 是一个面向对象的语言 ,在面向对象中 。一个类可以继承另一个类 ,在这里 我们的RestaurantTableViewController继承自UITableViewController 。它继承了UITableViewController所有的非私有方法和属性 。换句话说UITableViewController是RestaurantTableViewController的父类 。

storyboard中的表试图控制器并不了解RestaurantTableViewController ,我们要把他们关联起来 。如下图

这里写图片描述

还需要配置点东西 ,选择table view cell 。在属性编辑器中,把style改成Basic 给identifier 赋值为 Cell .

好的 ,用户界面差不多了 。我们来看看代码部分 。在项目导航栏选择RestaurantTableViewController.swift 。声明一个存数数据的变量。

var restaurantNames = ["Cafe Deadend", "Homei", "Teakha", "Cafe Loisl", "Petite Oyster", "ForKee Restaurant", "Po's Atelier", "Bourke Street Bakery","Haigh's Chocolate", "PalominoEspresso", "Upstate", "Traif", "Graham Avenue Meats", "Waffle & Wolf", "Five Leaves", "CafeLore", "Confessional", "Barrafina", "Donostia", "Royal Oak", "Thai Cafe"]

像前面说的 ,UITableViewController已经实现了UITableViewDataSource协议 。我们需要实现两个必须得方法 。

• tableView(_:numberOfRowsInSection:)
• tableView(_:cellForRowAtIndexPath:)

UITableViewController提供这两个方法的默认实现 。通常默认实现的不是我们想要的 ,我们需要去重写它们 。把下面的代码添加到RestaurantTableViewController.swift

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
    // Return the number of rows in the section.
    return self.restaurantNames.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath:
NSIndexPath) -> UITableViewCell {
    let cellIdentifier = "Cell"
    let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath:
    indexPath) as UITableViewCell
    // Configure the cell...
    cell.textLabel.text = restaurantNames[indexPath.row]
    cell.imageView.image = UIImage(named: "restaurant.jpg")
    return cell
}

在Swift中 ,想要重写父类的方法 ,只要在方法的最前面加一个override关键字就可以了。上面的代码和上一章讲的一样 。就不再详细解释了 。

改变下面的代码片段 , xcode自己生成的是返回 0 ,我们实现的表视图 需要1个section 。

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    // Return the number of sections.
    return 1
}

顺便说明一下 ,这个方法是可选的 。你删掉它 ,照样可以跑起来 。

最后,从这个地址下载图片包 https://www.dropbox.com/s/d1rwisj6pt89db3/restaurantimages.zip (又是被墙的地址 ) 然后把所有的图片拖到images.xcassets 。上章讲过 。现在Run app ,它看起来和上一张的一样 。


显示不同的图片

上一章的练习作业做了吗 ?我希望你想过这个问题 。有很多方式去做这件事 。我将会教你最简单的一种。 下载另一个图像包 https://www.dropbox.com/s/zzlsbz2c4p3pow1/restaurantimages.zip 然后把所有的图片拖到images.xcassets 。 里面包含一些相同的食物和餐馆的图片 。当然你也可以用你自己喜欢的图 。

这里写图片描述

在我们修改代码之前 , 先看看我们前面是怎么处理图像的

cell.imageView.image = UIImage(named: "restaurant.jpg")

上句话在cellForRowAtIndexPath:)方法中意思是给每行都显示restaurant.jpg 图片 。为了显示不同的图 ,我们需要修改这行代码 。像前面说的,这个方法在table显示每行的时候都会调用 。

如果你能理解餐馆名称是怎么显示的 ,你就会知道indexPath 对象可以得到表试图的当前行 。(indexPath.row)

因此,要显示不同的图,我们需要创建一个数组 ,存储不同的文件名 :

var restaurantImages = ["cafedeadend.jpg", "homei.jpg", "teakha.jpg", "cafeloisl.jpg",
"petiteoyster.jpg", "forkeerestaurant.jpg", "posatelier.jpg", "bourkestreetbakery.jpg",
"haighschocolate.jpg", "palominoespresso.jpg", "upstate.jpg", "traif.jpg",
"grahamavenuemeats.jpg", "wafflewolf.jpg", "fiveleaves.jpg", "cafelore.jpg",
"confessional.jpg", "barrafina.jpg", "donostia.jpg", "royaloak.jpg", "thaicafe.jpg"]

可以看到上面的代码 ,我们初始化了一个数组存储图像文件名 。 顺序和 餐馆名一样 。

最后修改tableView(_:cellForRowAtIndexPath:)的那句代码 :

cell.imageView.image = UIImage(named:restaurantImages[indexPath.row])

改完后 ,Run你的app现在应该显示不同的图了 。如图

这里写图片描述


自定义 Table View Cell

现在这个app看起来好一点了? 我们用自定义Table View Cell把它做得更好 。目前为止 ,我们一直利用的是默认的Table View Cell样式 。图片的目录和地址也是固定的 。如果你还想做点别的:

  • 改变Cell的高度

  • 让图片稍微大点

  • 显示更多关于餐馆的信息 , 比如地址和类型

  • 改变字体类型和大小

  • 显示圆形图片来代替方形图片

为了使你更好理解自定义cell 。 看一下下图 ,非常棒!是吧?

这里写图片描述


在storyboard中涉及Prototype Cell

prototype cell的魅力在于它允许开发者自定义表视图控制器内的单元格 。要构建一个自定义cell .你只需要在cell上添加其他得UI控件 (eg . UILabel UIButton)

首先改变cell的style从Basic改成Custom 。
这里写图片描述

为了容纳大一点的图片 ,我们需要改变cell的高度 。你需要改变表视图和cell的行高 。选择tableView 把 Row Height改成80 。

这里写图片描述

然后选择cell 点击”Size ” 。勾选 custom 把Row Height 改成80.
这里写图片描述

改好行高后 ,从对象库拖一个Image View放在cell原型上 。你可以调整他得大小和位置 。下图是我推荐的饿大小。你可以选择那个image view 。 点击”Size ” 检察框 。改变x,y,width,height的值 。

这里写图片描述

下一步 , 我们添加三个label :

  • Name –显示餐馆名

  • Location –显示地址

  • Type –类型

从对象库拖一个Label放在cell上 。 起名位Name 。在Size Inspector , 设置x 为 86 ,Y 为 9,Width 为205 ,Height 为 21 。 把字体改成”Avenir Next” (或者其他你喜欢的),字体大小改成16 points 。如图

这里写图片描述

再拖一个命名位”Location” 。在Size inspector , 设置x 86 ,y 35 , width 205 ,height 18 .字体大小13

最后一个label 命名位type 。 x 86 ,y 54 , width 205 ,height 21 .字体大小11


为自定义Cell建一个类

目前为止 ,我们设计了table Cell 。我们怎么去改变table cell的值呢 。这个值都是动态的 。

我们建一个类跟这个cell做一个关联 。这个类代表这个tableview cell的底层数据模型 。

像之前一样 , 新建一个 “Cocoa Touch Class” 文件 起名为”CustomTableViewCell” 。继承自UITableViewCell 。
这里写图片描述

然后打开这个文件,声明一些变量 :

@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var locationLabel: UILabel!
@IBOutlet weak var typeLabel: UILabel!
@IBOutlet weak var thumbnailImageView: UIImageView!

这个类提供自定义cell的数据模型 。在cell中 我们有四个可变的属性 。数据模型存储和提供要显示的值。

每个变量都将和界面上得对象进行连接 。把代码和界面连接后我们就可以动态改变界面的值了 。

回想下我们使用@IBAction添加按钮的行为 。 @IBOutlet关键字表示一个暴露出来的属性 。

在我们连接之前 。我们首先要设置自定义类。。

这里写图片描述

默认是连接UITableViewCell 类的 ,我们这里把它改成我们自己的CustomTableViewCell.


建立连接

下来我们需要建立cell中对象和代码的连接 。进入storyboard

像下图那样右击cell会弹出一个框。点击thumbnailImageView右边的圆圈 ,拖到prototype cell 的 UIImageView对象上 。然后放开 ,Xcode会自动为你建立连接。

这里写图片描述

对其他属性重复上面的过程 :

• locationLabel - 连接 Location
• nameLabel - 连接Name label
• typeLabel - 连接 Type

这里写图片描述

连接完以后,应该是上图的样子 。


为Table View Controller写代码

我们已经完成自定义table cell的设计和代码部分。 下来我们最最后的改动 。在RestaurantTableViewController中 ,我们依然用的UITableViewCell来显示内容 。我们需要改一行代码以便使用我们自定义的cell 。tableView(_:cellForRowAtIndexPath:)方法的第二行 。现在时这样子的:

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

前面已经解释过dequeueReusableCellWithIdentifier方法的含义 。这个as UITableViewCell是什么意思呢 ? 这个方法是非常灵活的 。它可以返回任何cell的类型 。swift 使用as关键字来做类型转换 。前面我们使用默认的cell ,现在我们要使用自定义的CustomTableViewCell, 所以我们需要转换成CustomTableViewCell 。因此把上面的代码改成这样 ;

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

你现在运行app ,看起来有点诡异 。我们需要再改变些代码。下面的代码用来设置餐馆名称和图片的 。

cell.textLabel.text = restaurantNames[indexPath.row]
cell.imageView.image = UIImage(named:restaurantImages[indexPath.row])

textLabel 和 imageView属性来自 UITableViewCell ,我们现在要使用自定义的CustomTableViewCell, 我们需要使用我们自定义类中的属性 。修改上面的代码 :

// Configure the cell...
cell.nameLabel.text = restaurantNames[indexPath.row]
cell.thumbnailImageView.image = UIImage(named:restaurantImages[indexPath.row])

现在Run 你的app , 应该看起来是下图的样子 。试着横屏。也显示的很正常

这里写图片描述

这已经是一个巨大的进步了 ,我们还想再改进一点 ,把图片显示成圆形 。


圆形图片

你可能从来没听过CALyer 类 ,也许你已经用过她了。每一个UIkit的view都被一个CALyer所支持 。layer对象处理管理view的显示动画 。

layer对象提供很多属性控制view的内容 :

• Background color(背景色)
• Border and border width (边框和边框宽度)
• Shadow color, width, etc (阴影颜色、宽度等)
• Opacity (透明度)
• Corner radius (圆角)

Corner radius 就是我们要用到的属性 。理解一个东西用处的最好方式就是使用它 。tableView(_:cellForRowAtIndexPath:)方法的 return cell 前加上如下代码 :

cell.thumbnailImageView.layer.cornerRadius = cell.thumbnailImageView.frame.size.width / 2
cell.thumbnailImageView.clipsToBounds = true 

如前所述 , 这是对任何view对象的layer的属性 。第一行代码设置的是圆角半径 。为了从一个正方形图形中得到圆形图形 。圆角半径设位宽度的一半 。后面我们把clipsToBounds设置为true 将多余的部分裁剪 。

现在运行你的app 。所有图像都变成了圆形 。我们仅仅用了两行代码让它变成圆形 ,根本不需要Photoshop 。

这里写图片描述

你可以自由改变圆角半径的值, 然后运行看看效果 。

cell.thumbnailImageView.layer.cornerRadius = 10.0

你的作业

你也许会奇怪为啥app 只是简单的展示”Location” 和 “Type” 对每一行 。练习作业 ,我希望你能修改这个问题 。尝试改变你的代码 ,作为提示 。我给你提供两个数组

var restaurantLocations = ["Hong Kong", "Hong
Kong", "Hong Kong", "Hong Kong", "Hong Kong", "Hong
Kong", "Hong Kong", "Sydney", "Sydney", "Sydney",
"New York", "New York", "New York", "New York", "New
York", "New York", "New York", "London", "London",
"London", "London"]
var restaurantTypes = ["Coffee & Tea Shop",
"Cafe", "Tea House", "Austrian / Causual Drink",
"French", "Bakery", "Bakery", "Chocolate", "Cafe",
"American / Seafood", "American", "American",
"Breakfast & Brunch", "Coffee & Tea", "Coffee &
Tea", "Latin American", "Spanish", "Spanish",
"Spanish", "British", "Thai"]

你也可以重新惹急cell 。尝试去做一个下图这种 ?

这里写图片描述


总结

恭喜你 ! 你已经取得了巨大的进步 。如果你已经了解了自定义cell的本末 。那么你已经准备好去做一些更棒的UI 。table view是很多app的骨干 。除了游戏app 。你很可能会在你的app中使用tableview 。表视图可能会有点复杂一些对你来说 。所以花点时间去做下练习 ,动手是最好的学习方式 。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值