版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.11.22 星期四 |
前言
iOS中有关视图控件用户能看到的都在UIKit框架里面,用户交互也是通过UIKit进行的。感兴趣的参考上面几篇文章。
1. UIKit框架(一) —— UIKit动力学和移动效果(一)
2. UIKit框架(二) —— UIKit动力学和移动效果(二)
3. UIKit框架(三) —— UICollectionViewCell的扩张效果的实现(一)
4. UIKit框架(四) —— UICollectionViewCell的扩张效果的实现(二)
5. UIKit框架(五) —— 自定义控件:可重复使用的滑块(一)
6. UIKit框架(六) —— 自定义控件:可重复使用的滑块(二)
开始
首先看一下写作环境
Swift 4.2, iOS 12, Xcode 10
如果您之前曾创建过自定义table view cell
,那么您可能需要花费大量时间在代码中调整table view cell
。 您甚至可能熟悉必须手动计算每个label
,image view
, text field
的高度 - 以及单元格内的所有其他内容。
坦率地说,这种方法令人难以置信且容易出错。 在这个自定义table view cell
教程中,您将学习如何动态创建和调整表格视图单元格以适合其内容。 你可能会想:“这需要做很多工作!”不!
本教程假设您基本熟悉Auto Layout
和UITableView
。
回到iOS 6时代,Apple推出了一项精彩的新技术:Auto Layout
。 开发人员欢欣鼓舞。
好吧,这可能是一个延伸,但这是一个大问题。
虽然它激发了开发人员的希望,但Auto Layout
仍然很繁琐。 手动处理自动布局过去是,现在仍然是iOS开发中冗长的一个很好的例子。 在设置约束时,Interface Builder
最初效率也很低。
快进到现在。 通过Interface Builder
的所有改进,可以轻松使用Auto Layout
来创建动态尺寸的自定义表格视图单元格!
除了一些细节之外,你真正需要做的就是:
- 1) 对表格视图单元格内的UI元素使用自动布局。
- 2) 将
table view
的rowHeight
设置为UITableViewAutomaticDimension
。 - 3) 设置
estimatedRowHeight
或实现高度估计代理方法。
但是你现在不想深入研究理论,对吗? 您已准备好开始编写代码 - 因此与项目开展业务。
Tutorial App Overview - 教程应用程序概述
想象一下,你有一个电影疯狂的客户想要一个应用程序来炫耀一些最喜欢的电影导演和一些他们最突出的工作。实际上,不是任何导演,只是他们最喜欢的导演。
Oui,是的。电影制作的导演理论在20世纪40年代在法国出现,它基本上意味着导演是电影背后的推动创作力量。不是每个导演都是导演 - 只有那些用各自风格标记每部电影的导演。想想Tarantino or Scorsese
。并不是每个人都同意这个理论 - 不要让你的编剧朋友开始。但是,客户总是对的,所以你准备开始滚动了。
有一个问题:“我们开始制作应用程序,但我们对如何在表格视图中显示内容感到困惑,”您的客户承认。 “我们的表视图单元格必须动态调整大小!你可以吗?“
你突然觉得穿上漂亮的贝雷帽并开始大喊大叫的冲动!
但是你不需要噱头来成为你客户的iOS导演 - 你的编程技巧就足够了!
首先,打开Auteur-starter
项目。
在Auteur-starter
项目中,在Auteurs
项目下的Views
组中打开Main.storyboard
。 你会看到三个场景:
从左到右,它们是:
- 顶级导航控制器。
-
AuteurListViewController
显示了一个导演列表。 -
AuteurDetailViewController
显示导演的电影和每部电影的信息。
构建并运行。 你会看到AuteurListViewController
显示了一个导演列表。 选择第一位导演Wes Anderson
。 该应用程序将转到AuteurDetailViewController
以显示所选导演电影的列表:
不仅应用程序缺少每个导演和每部电影的图像,您尝试显示的信息也被切断了! 每条信息和图像都是不同的大小,因此您不能只增加表视图单元格高度并封装调用。 根据每个cell的内容,您的cell高度需要是动态的。
您将首先在AuteurListViewController
中实现动态单元格高度。
Creating Self-Sizing Table View Cells - 创建动态尺寸表格视图单元格
要使动态单元格高度正常工作,您需要创建自定义表格视图单元格并使用正确的Auto Layout
约束进行设置。
在项目导航器中,选择Views
组,然后按Command-N
在该组中创建新文件。 创建一个名为AuteurTableViewCell
的新Cocoa Touch
类,并使其成为UITableViewCell
的子类。 确保未选中Also create XIB file
并将语言设置为Swift
。
首先,打开AuteurTableViewCell.swift
。 然后,删除两个自动生成的方法并添加以下属性:
@IBOutlet weak var bioLabel: UILabel!
接下来,打开Main.storyboard
并在Auteurs
场景的表视图中选择单元格。 在Identity inspector
中,将Class
更改为Auteurs uiTableViewCell
:
将新的UILabel
拖放到单元格上,然后将文本设置为Bio
。 在Attributes inspector
检查器中将新label的Lines
属性(label最多可以具有的行数)设置为0
。 它应该如下所示:
对于动态大小的单元格,设置行数非常重要。 行数设置为0的label将根据显示的文本数量增长。 将行数设置为任何其他数字的标签将在文本超出可用行时截断文本。
将AuteurTableViewCell
的bioLabel
的outlet
连接到cell上的label。 快速执行此操作的一种方法是右键单击Document Outline
中的Cell
,然后从弹出菜单中outlet
列表下的bioLabel
右侧的空白圆圈单击并拖动到您布置的label:
让Auto Layout
在UITableViewCell
上运行的技巧是确保你有限制来固定所有方面的每个子视图 - 也就是说,每个子视图应该有leading, top, trailing and bottom
约束。 然后,子视图的固有高度将用于指示每个单元格的高度。 你现在就做。
选择bio label
,然后按故事板底部的Pin
按钮。 在此菜单中,选择菜单顶部的四条虚线。 接下来,将leading and trailing
更改为8
,然后单击Add Constraints
。 它看起来像这样:
这确保了无论cell多大或多小,bio label
始终如下:
-
top and bottom
边距为0点。 -
leading and trailing
边缘8点。
评论:这是否满足以前的
Auto Layout
标准?
- 1) 每个子视图是否都有限制所有方向的约束? 是的
- 2) 是否存在从
contentView
的顶部到底部的约束? 是
bio label
通过0点连接到顶部和底部边距,因此自动布局现在可以确定单元格的高度!
太棒了,您的AuteurTableViewCell
已经设置好了! 如果你现在构建并运行应用程序,你会看到......
......哇,那不对!
在单元格变为动态之前,需要编写一些代码。
Configuring the Table View - 配置Table View
首先,您需要配置Table View
以正确使用自定义单元格。
打开AuteurListViewController.swift
(在ViewControllers
组中)并用以下内容替换tableView(_:cellForRowAt :)
:
func tableView(
_ tableView: UITableView,
cellForRowAt indexPath: IndexPath
) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(
withIdentifier: "Cell",
for: indexPath) as! AuteurTableViewCell
let auteur = auteurs[indexPath.row]
cell.bioLabel.text = auteur.bio
cell.bioLabel.textColor = UIColor(red:0.75, green:0.75, blue:0.75, alpha:1.0)
return cell
}
上面的代码非常简单:您将单元格出列,使用文本颜色设置其信息并返回单元格。
在AuteurListViewController.swift
中,在viewDidLoad()
的底部添加以下两行代码:
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 600
当您将行高设置为UITableViewAutomaticDimension
时,表视图将被告知使用自动布局约束及其单元格的内容来确定每个单元格的高度。
为了使table view
执行此操作,您还必须提供estimatedRowHeight
。 在这种情况下,600只是一个在这个特定实例中运行良好的任意值。 对于您自己的项目,您应该选择一个更符合您将显示的数据类型的值。
构建和运行,你现在应该看到每个auteur’s bio
。
那看起来更好! 但是你知道什么会让这个应用程序更具戏剧性吗? 黑暗背景。
打开Main.storyboard
,在Auteur
场景的表视图中选择单元格。 在Attributes inspector
中,找到Background
下拉菜单:
单击Background
,然后选择Custom
,在弹出的窗口中,选择RGB Sliders
选项卡,然后在Hex Color#
字段中输入161616
- 这将为您提供一个漂亮的黑色背景(纯黑色对于导演来说不够酷)。
按Return
键,构建并运行您的应用程序。 它应该如下所示:
Adding Images - 添加图像
很高兴您现在可以阅读每位艺术家的整个作品,但还有更多数据需要展示。 每位艺术家都有一个图像和一个名字。 这个应用程序使用外部API来检索信息,因此也有一个源标签。 使用这些额外的数据将使应用程序看起来更好。
您需要将image view
添加到AuteurTableViewCell
,并为auteur
的名字i添加另一个label
。 您还应添加源label
以正确记入照片。 打开AuteurTableViewCell.swift
并添加以下属性:
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var source: UILabel!
@IBOutlet weak var auteurImageView: UIImageView!
注意:
image view
的变量名称是auteurImageView
而不是imageView
,因为UITableViewCell
上已经有一个imageView
属性。
现在,调整bio label
。 很难在Main.storyboard
中看到黑暗的cell背景。 打开Main.storyboard
,选择bio label
并为文本选择较浅的颜色。
注意:在
Main.storyboard
中设置bioLabel
颜色并不重要,因为AuteurList ViewController.swift
中的代码将在运行时覆盖它:
cell.bioLabel.textColor = UIColor(red:0.75, green:0.75,
blue:0.75, alpha:1.0)
现在,从bio label
中删除约束,以便您可以向cell添加更多元素。 在Document Outline
中,找到Bio Label
下的Constraints
。 选择四个约束中的每一个(按住Shift
键将它们全部选中),然后按Delete
。 暂时忽略任何自动布局警告。
在添加更多元素之前,您需要一些空间。 从Document Outline
中选择Cell
。 然后,在Size inspector
中,将Row Height
更改为450
。
现在,你将填充这个空间:
- 将
ImageView
拖到单元格中。 - 将
Label
拖到单元格中。 然后,将其文本设置为Name
,将其颜色设置为白色。 - 将另一个
Label
拖到单元格中。 然后,将其文本设置为Source
,将其Font设置为System 13.0
,将其颜色设置为白色。
按以下顺序从上到下移动元素(您将很快添加约束):
- 1)
ImageView
- 2)
Name
- 3)
Bio
- 4)
Source
接下来,使用与bio label
相同的技术连接新image view and label
的outlets
:
现在,是时候设置更多约束了。 从源label向上移动开始,使用Pin
菜单添加这些垂直约束:
- 将
source label
的bottom edge
固定为超视图的16个点。 - 将
source label
的top edge
固定到bio label
底部的8个点。 - 将
bio label
的top edge
固定到name label
底部的8个点。 - 将
name label
的top edge
固定在image view
底部的8个点上。 - 将
image view
的top edge
固定为0,从content view
的顶部开始。
现在,添加水平约束。 首先,使用Pin
菜单:
- 将
name label
的leading edge
固定为superview
的8个点。 - 将
name label
的trailing edge
固定为superview
的8个点。 -
Shift
键 - 单击name label
,bio label and source label
,然后从“Pin”菜单中选择“Equal Width”。 - 将
image view
设置为300点的高度和宽度。
然后,使用Align
菜单将所有项目居中:
- 按住Shift键并单击
image view, name label, bio label and source label
,然后从Align menu
菜单中选择Horizontally in Container
。
最后,您需要为name label and bio label
设置hugging vertical
优先级。 在 Size inspector
检查器中选择nameLabel
,然后向下滚动,直到看到Content Hugging Priority
。 默认情况下,水平和垂直都将设置为251
。 将name label
的垂直优先级更改为253
。
现在,选择bio label
并将其Vertical
优先级设置为252
,您现在已完成故事板。
注意:等等,
hugging
是什么? 这是一张浪漫的照片吗?
不是。 在content hugging
上设置更高的优先级意味着视图将抵抗比其intrinsic size
更大的增长。 你告诉故事板要让你的单元格高450点,这大于你视图的intrinsic size
。 设置vertical content hugging
优先级告诉Xcode在需要填充空间时要扩展哪个视图。
在设置bioLabel
的文本后,打开AuteurListViewController.swift
并将以下两行代码添加到tableView(_:cellForRowAt :)
:
cell.auteurImageView.image = UIImage(named: auteur.image)
cell.nameLabel.text = auteur.name
cell.source.text = auteur.source
然后,在设置textColor
后添加这些行:
cell.nameLabel.textColor = .white
cell.bioLabel.textColor = UIColor(red:0.75, green:0.75, blue:0.75, alpha:1.0)
cell.source.textColor = UIColor(red:0.74, green:0.74, blue:0.74, alpha:1.0)
cell.source.font = UIFont.italicSystemFont(ofSize: cell.source.font.pointSize)
cell.nameLabel.textAlignment = .center
cell.selectionStyle = .none
构建并运行您的应用程序
看起来不错。 但是auteurs
不是正方形,所以让我们调整tableView(_:cellForRowAt :)
在return cell
之前添加以下代码:
cell.auteurImageView.layer.cornerRadius = cell.auteurImageView.frame.size.width / 2
然后,返回Main.storyboard
并选择image view
。 然后,在Attributes inspector
中选择Clip to Bounds
选项:
再次构建并运行您的应用程序,以便在适合MGM lion
的圆形frame中查看您的导演图像:
你刚刚完成了对AuteurListViewController
的约束,但是眼尖的读者可能已经注意到Xcode会发出警告。
如果单击警告,您将获得更多信息。 事实证明,布局在英语中运行良好,但如果您将应用本地化为使用从右到左阅读的语言,则可能会出现问题。
Xcode提供解决问题的方法。 但您可以通过调整其中一个name label
约束来自己完成。 返回Main.storyboard
并选择name label
。 在Size Inspector
中,找到Trailing Space
约束,然后单击Edit
。
将运算符从等于更改为大于等于。
Xcode警告消失了!
Creating Dynamic Height - 创建动态高度
如果你从头开始回想起来,选择一个导演就会出现一个视图控制器,显示选定的导演电影。 此表视图中的cell也需要具有动态高度。
与之前一样,第一步是创建UITableViewCell
的另一个子类。
在项目导航器中,选择Views
组,然后按Command-N
在该组中创建新文件。 创建一个名为FilmTableViewCell
的新Cocoa Touch
类,并使其成为UITableViewCell
的子类。
打开FilmTableViewCell.swift
,和之前一样,删除FilmTableViewCell
中的两个自动生成的方法,然后添加以下属性:
@IBOutlet weak var filmImageView: UIImageView!
@IBOutlet weak var filmTitleLabel: UILabel!
@IBOutlet weak var moreInfoTextView: UITextView!
打开Main.storyboard
并在Auteur Detail View Controller
场景的表视图中选择单元格。 将单元格的Custom Class
设置为FilmTableViewCell
,然后将行高更改为300
,以便为自己提供足够的工作空间。
现在,拖出Image View, a Label and a Text View
。 将它们放置如下图所示(文本视图位于最底部):
将text view
的文本更改为Tap For Details >
,将label更改为Name
。 将图像视图的模式更改为Aspect Fit
。 在Attribute inspector
中选择文本视图。 然后,将其对齐方式更改为Centered
,将文本颜色设置为红色并禁用滚动:
禁用滚动与将标签设置为0
行类似。 禁用滚动后,文本视图知道增大其大小以适合其所有内容,因为用户将无法滚动文本。
再远远超过禁用滚动的位置,请从User Interaction Enabled
中删除对号。 这将允许触摸通过文本视图并触发单元格本身的选择。 然后,将background color
设置为Clear Color
。
现在,在Auteur Detail View Controller
场景中选择单元格,并将背景设置为Hex Color#161616
,就像在Auteur
场景上一样。选择name
标签并将文本颜色设置为白色,以便在Main.storyboard
上查看。
将三个元素与相应的outlets
连接,就像使用第一个单元一样。
现在,您将添加约束。从text view
开始并向上移动:
- 将
text view
的bottom edge
固定为0,从content view
的下边距开始。 - 将
text view
的leading and trailing edges
固定为content view
的leading and trailing edges
的8个点。 - 将
text view
的top edge
固定在label底部的8个点上。 - 将
label
的top edge
固定在image view底部的8个点上。 - 通过在
Align
菜单中选择Horizontally in Container
来居中名称标签。 - 选择
name
标签和image view
(使用Shift键单击),然后从“Pin”
菜单中选择“Equal Widths”
。 - 将
image view
的top edge
固定为0,从内容视图的上边距开始。 - 将
image view
的leading and trailing edges
固定为内容视图的前缘和后缘的8个点
你现在已经完成了sb。正如您必须使用以前的视图控制器一样,动态单元格高度也需要一些代码。
打开AuteurDetailViewController.swift
并用以下代码替换tableView(_:cellForRowAt :)
:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
let cell = tableView
.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! FilmTableViewCell
let film = selectedAuteur.films[indexPath.row]
cell.filmTitleLabel.text = film.title
cell.filmImageView.image = UIImage(named: film.poster)
cell.filmTitleLabel.textColor = .white
cell.filmTitleLabel.textAlignment = .center
cell.moreInfoTextView.textColor = .red
cell.selectionStyle = .none
return cell
}
这应该看起来非常熟悉了。 您正在出列并构建单元格,获取要显示的模型结构的引用,然后在返回之前配置单元格。
现在,在同一个类的viewDidLoad()
中,将以下代码添加到方法的末尾:
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 300
构建并运行您的应用程序。 点击其中一位导演观看他们的电影。
不错,但通过添加扩展单元格以显示有关每项作品的更多信息,将其提升到新的水平。 你的客户会喜欢这个!
Expanding Cells - 扩展单元格
由于单元格高度由自动布局约束和每个界面元素的内容驱动,因此扩展单元格应该像在用户点击该单元格时向text view
添加更多文本一样简单。
打开AuteurDetailViewController.swift
并添加以下扩展:
extension AuteurDetailViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// 1
guard let cell = tableView.cellForRow(at: indexPath) as? FilmTableViewCell else {
return
}
var film = selectedAuteur.films[indexPath.row]
// 2
film.isExpanded = !film.isExpanded
selectedAuteur.films[indexPath.row] = film
// 3
cell.moreInfoTextView.text = film.isExpanded ? film.plot : moreInfoText
cell.moreInfoTextView.textAlignment = film.isExpanded ? .left : .center
cell.moreInfoTextView.textColor = film.isExpanded ?
UIColor(red:0.75, green:0.75, blue:0.75, alpha:1.0) :
.red
// 4
tableView.beginUpdates()
tableView.endUpdates()
// 5
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
}
}
这是发生了什么:
- 1) 您可以在
tableView
中查询所选indexPath
处的单元格的引用,然后获取相应的Film对象。 - 2) 您切换
Film
对象的isExpanded
状态并将其添加回数组,这是必需的,因为结构是值类型。 - 3) 接下来,根据作品是否展开,更改单元格的
text view
。如果是,则设置text view
以显示电影的信息属性。您还可以将文本对齐方式更改为.left
,将其颜色更改为浅灰色。如果未展开,则将文本设置回“Tap for Details >”
,将对齐设置回.center
,颜色返回红色。 - 4)
table view
现在需要刷新单元格高度。调用beginUpdates()
和endUpdates()
将强制table view
以动画方式刷新高度。 - 5) 最后,您告诉
table view
以动画方式将所选行滚动到table view
的顶部。
现在在tableView(_:cellForRowAt :)
中,在返回单元格之前,在末尾添加以下三行:
cell.moreInfoTextView.text = film.isExpanded ? film.plot : moreInfoText
cell.moreInfoTextView.textAlignment = film.isExpanded ? .left : .center
cell.moreInfoTextView.textColor = film.isExpanded ?
UIColor(red:0.75, green:0.75, blue:0.75, alpha:1.0) :
.red
此代码将导致正在重用的单元格正确记住它以前是否处于展开状态。
构建并运行应用程序。 当您点击film
单元格时,您会看到它会扩展以容纳全文。 但是图像有点怪异。
这不难解决! 打开Main.storyboard
,在FilmTableViewCell
中选择image view
,然后打开Size inspector
。 将Content Hugging Priority
和Content Compression Resistance Priority
更改为下图所示的值:
将Vertical Content Hugging Priority
设置为252
将有助于image view
hug
其内容,而不是在动画期间拉伸。将Vertical Compression Resistance Priority
设置为1000
可防止图像在其周围生长其他界面元素时被压缩。
构建并运行应用程序。选择一个导演并点击电影。您应该看到一些非常平滑的cell扩张,展示每部电影的信息。
Implementing Dynamic Type - 实现动态类型
您已向客户展示了自己的进步,他们非常喜欢它!但他们有一个最终要求。他们希望该应用程序支持更大的文本辅助功能。该应用程序需要根据客户的首选阅读尺寸进行调整。
动态类型(Dynamic Type)
在iOS 7中引入,使这项任务变得简单。它使开发人员能够为不同的文本块(如标题或正文)指定不同的文本样式,并在用户更改设备设置中的首选大小时自动调整该文本。
回到Main.storyboard
和Auteurs
场景。选择name label
,然后在Attributes inspector
中,完成以下步骤:
- 1) 单击
Font
中的“T”
图标。 - 2) 从
Text Styles
下选择Headline
。 - 3) 选中
Automatically Adjusts Font
。 - 4) 将
Lines
设置为0。
对bio label
执行相同操作,但在Text Styles
下选择Body
而不是Headline
。
在Auteur Detail View Controller
场景中,对name label
执行相同的步骤,将其Text Style
设置为Headline
。 将Text View
的文本样式设置为Body
(没有Lines
调整,因为这不是label
)。
这就是您需要做的所有事情,以使应用程序更容易访问。 构建并运行您的应用程序。
单击模拟器上的Home
按钮并打开“设置”应用程序,然后单击General ▸ Accessibility ▸ Larger Text
并向右拖动滑块以将文本大小增加到较大的设置:
然后,回到Auteurs
应用程序'你的文字现在应该显得更大。 由于您在获取动态大小的单元格方面的工作,table view
看起来仍然很棒:
后记
本篇主要讲述了动态尺寸UITableViewCell的实现,感兴趣的给个赞或者关注~~~