UIKit框架(七) —— 动态尺寸UITableViewCell的实现(一)

版本记录

版本号 时间
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。 您甚至可能熟悉必须手动计算每个labelimage view, text field的高度 - 以及单元格内的所有其他内容。

坦率地说,这种方法令人难以置信且容易出错。 在这个自定义table view cell教程中,您将学习如何动态创建和调整表格视图单元格以适合其内容。 你可能会想:“这需要做很多工作!”不!

本教程假设您基本熟悉Auto LayoutUITableView

回到iOS 6时代,Apple推出了一项精彩的新技术:Auto Layout。 开发人员欢欣鼓舞。

好吧,这可能是一个延伸,但这是一个大问题。

虽然它激发了开发人员的希望,但Auto Layout仍然很繁琐。 手动处理自动布局过去是,现在仍然是iOS开发中冗长的一个很好的例子。 在设置约束时,Interface Builder最初效率也很低。

快进到现在。 通过Interface Builder的所有改进,可以轻松使用Auto Layout来创建动态尺寸的自定义表格视图单元格!

除了一些细节之外,你真正需要做的就是:

  • 1) 对表格视图单元格内的UI元素使用自动布局。
  • 2) 将table viewrowHeight设置为UITableViewAutomaticDimension
  • 3) 设置estimatedRowHeight或实现高度估计代理方法。

但是你现在不想深入研究理论,对吗? 您已准备好开始编写代码 - 因此与项目开展业务。


Tutorial App Overview - 教程应用程序概述

想象一下,你有一个电影疯狂的客户想要一个应用程序来炫耀一些最喜欢的电影导演和一些他们最突出的工作。实际上,不是任何导演,只是他们最喜欢的导演。

Oui,是的。电影制作的导演理论在20世纪40年代在法国出现,它基本上意味着导演是电影背后的推动创作力量。不是每个导演都是导演 - 只有那些用各自风格标记每部电影的导演。想想Tarantino or Scorsese。并不是每个人都同意这个理论 - 不要让你的编剧朋友开始。但是,客户总是对的,所以你准备开始滚动了。

有一个问题:“我们开始制作应用程序,但我们对如何在表格视图中显示内容感到困惑,”您的客户承认。 “我们的表视图单元格必须动态调整大小!你可以吗?“

你突然觉得穿上漂亮的贝雷帽并开始大喊大叫的冲动!

3691932-351e076536ba2aa1.png

但是你不需要噱头来成为你客户的iOS导演 - 你的编程技巧就足够了!

首先,打开Auteur-starter项目。

Auteur-starter项目中,在Auteurs项目下的Views组中打开Main.storyboard。 你会看到三个场景:

3691932-cb5cda08b8bfc1b9.png

从左到右,它们是:

  • 顶级导航控制器。
  • AuteurListViewController显示了一个导演列表。
  • AuteurDetailViewController显示导演的电影和每部电影的信息。

构建并运行。 你会看到AuteurListViewController显示了一个导演列表。 选择第一位导演Wes Anderson。 该应用程序将转到AuteurDetailViewController以显示所选导演电影的列表:

3691932-3c6ee122819bcacc.png

不仅应用程序缺少每个导演和每部电影的图像,您尝试显示的信息也被切断了! 每条信息和图像都是不同的大小,因此您不能只增加表视图单元格高度并封装调用。 根据每个cell的内容,您的cell高度需要是动态的。

您将首先在AuteurListViewController中实现动态单元格高度。


Creating Self-Sizing Table View Cells - 创建动态尺寸表格视图单元格

要使动态单元格高度正常工作,您需要创建自定义表格视图单元格并使用正确的Auto Layout约束进行设置。

在项目导航器中,选择Views组,然后按Command-N在该组中创建新文件。 创建一个名为AuteurTableViewCell的新Cocoa Touch类,并使其成为UITableViewCell的子类。 确保未选中Also create XIB file并将语言设置为Swift

3691932-7592056ec8ec051b.png

首先,打开AuteurTableViewCell.swift。 然后,删除两个自动生成的方法并添加以下属性:

@IBOutlet weak var bioLabel: UILabel!

接下来,打开Main.storyboard并在Auteurs场景的表视图中选择单元格。 在Identity inspector中,将Class更改为Auteurs uiTableViewCell

3691932-3b407f5e399caf14.png

将新的UILabel拖放到单元格上,然后将文本设置为Bio。 在Attributes inspector检查器中将新label的Lines属性(label最多可以具有的行数)设置为0。 它应该如下所示:

3691932-15356616fde03dbb.png

对于动态大小的单元格,设置行数非常重要。 行数设置为0的label将根据显示的文本数量增长。 将行数设置为任何其他数字的标签将在文本超出可用行时截断文本。

AuteurTableViewCellbioLabeloutlet连接到cell上的label。 快速执行此操作的一种方法是右键单击Document Outline中的Cell,然后从弹出菜单中outlet列表下的bioLabel右侧的空白圆圈单击并拖动到您布置的label:

3691932-b26bf36a7b03dfc6.png

Auto LayoutUITableViewCell上运行的技巧是确保你有限制来固定所有方面的每个子视图 - 也就是说,每个子视图应该有leading, top, trailing and bottom约束。 然后,子视图的固有高度将用于指示每个单元格的高度。 你现在就做。

选择bio label,然后按故事板底部的Pin按钮。 在此菜单中,选择菜单顶部的四条虚线。 接下来,将leading and trailing更改为8,然后单击Add Constraints。 它看起来像这样:

3691932-922cf008c5414c5c.png

这确保了无论cell多大或多小,bio label始终如下:

  • top and bottom边距为0点。
  • leading and trailing边缘8点。

评论:这是否满足以前的Auto Layout标准?

  • 1) 每个子视图是否都有限制所有方向的约束? 是的
  • 2) 是否存在从contentView的顶部到底部的约束? 是
    bio label通过0点连接到顶部和底部边距,因此自动布局现在可以确定单元格的高度!

太棒了,您的AuteurTableViewCell已经设置好了! 如果你现在构建并运行应用程序,你会看到......

3691932-67c4be66892d56ac.png

......哇,那不对!

在单元格变为动态之前,需要编写一些代码。


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

3691932-d3b3ad7761ea5056.png

那看起来更好! 但是你知道什么会让这个应用程序更具戏剧性吗? 黑暗背景。

打开Main.storyboard,在Auteur场景的表视图中选择单元格。 在Attributes inspector中,找到Background下拉菜单:

3691932-67bb96ae561112cc.png

单击Background,然后选择Custom,在弹出的窗口中,选择RGB Sliders选项卡,然后在Hex Color#字段中输入161616 - 这将为您提供一个漂亮的黑色背景(纯黑色对于导演来说不够酷)。

3691932-88f6db5f55484af0.png

Return键,构建并运行您的应用程序。 它应该如下所示:

3691932-d205014509faf04d.png

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并为文本选择较浅的颜色。

3691932-7bc6d87f115f8979.png

注意:在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。 暂时忽略任何自动布局警告。

3691932-6a58533cecdac7b3.png

在添加更多元素之前,您需要一些空间。 从Document Outline中选择Cell。 然后,在Size inspector中,将Row Height更改为450

3691932-0f2b5ed304d55404.png

现在,你将填充这个空间:

  • ImageView拖到单元格中。
  • Label拖到单元格中。 然后,将其文本设置为Name,将其颜色设置为白色。
  • 将另一个Label拖到单元格中。 然后,将其文本设置为Source,将其Font设置为System 13.0,将其颜色设置为白色。

按以下顺序从上到下移动元素(您将很快添加约束):

  • 1) ImageView
  • 2) Name
  • 3) Bio
  • 4) Source

接下来,使用与bio label相同的技术连接新image view and labeloutlets

3691932-5aff11b09b88779f.png

现在,是时候设置更多约束了。 从源label向上移动开始,使用Pin菜单添加这些垂直约束:

  • source labelbottom edge固定为超视图的16个点。
  • source labeltop edge固定到bio label底部的8个点。
  • bio labeltop edge固定到name label底部的8个点。
  • name labeltop edge固定在image view底部的8个点上。
  • image viewtop edge固定为0,从content view的顶部开始。

现在,添加水平约束。 首先,使用Pin菜单:

  • name labelleading edge固定为superview的8个点。
  • name labeltrailing edge固定为superview的8个点。
  • Shift键 - 单击name labelbio 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

3691932-197cb6b8669a4b87.png

现在,选择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

构建并运行您的应用程序

3691932-ec6d7351a0a451e6.png

看起来不错。 但是auteurs不是正方形,所以让我们调整tableView(_:cellForRowAt :)return cell之前添加以下代码:

cell.auteurImageView.layer.cornerRadius = cell.auteurImageView.frame.size.width / 2

然后,返回Main.storyboard并选择image view。 然后,在Attributes inspector中选择Clip to Bounds选项:

3691932-4228c9be91dc03ed.png

再次构建并运行您的应用程序,以便在适合MGM lion的圆形frame中查看您的导演图像:

3691932-bca50ac94608667e.png

你刚刚完成了对AuteurListViewController的约束,但是眼尖的读者可能已经注意到Xcode会发出警告。

3691932-c7ceb47b83df9860.png

如果单击警告,您将获得更多信息。 事实证明,布局在英语中运行良好,但如果您将应用本地化为使用从右到左阅读的语言,则可能会出现问题。

3691932-1accbeefd228d31a.png

Xcode提供解决问题的方法。 但您可以通过调整其中一个name label约束来自己完成。 返回Main.storyboard并选择name label。 在Size Inspector中,找到Trailing Space约束,然后单击Edit

3691932-2b4fc86fa96c6039.png

将运算符从等于更改为大于等于。

3691932-5713b4b4494bb3d5.png

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。 将它们放置如下图所示(文本视图位于最底部):

3691932-0ec3bc980879074c.png

text view的文本更改为Tap For Details >,将label更改为Name。 将图像视图的模式更改为Aspect Fit。 在Attribute inspector中选择文本视图。 然后,将其对齐方式更改为Centered,将文本颜色设置为红色并禁用滚动:

3691932-61baf5abb1e2b043.png

禁用滚动与将标签设置为0行类似。 禁用滚动后,文本视图知道增大其大小以适合其所有内容,因为用户将无法滚动文本。

再远远超过禁用滚动的位置,请从User Interaction Enabled中删除对号。 这将允许触摸通过文本视图并触发单元格本身的选择。 然后,将background color设置为Clear Color

3691932-508a3cc75f185156.png

现在,在Auteur Detail View Controller场景中选择单元格,并将背景设置为Hex Color#161616,就像在Auteur场景上一样。选择name标签并将文本颜色设置为白色,以便在Main.storyboard上查看。

将三个元素与相应的outlets连接,就像使用第一个单元一样。

现在,您将添加约束。从text view开始并向上移动:

  • text viewbottom edge固定为0,从content view的下边距开始。
  • text viewleading and trailing edges固定为content viewleading and trailing edges的8个点。
  • text viewtop edge固定在label底部的8个点上。
  • labeltop edge固定在image view底部的8个点上。
  • 通过在Align菜单中选择Horizontally in Container来居中名称标签。
  • 选择name标签和image view(使用Shift键单击),然后从“Pin”菜单中选择“Equal Widths”
  • image viewtop edge固定为0,从内容视图的上边距开始。
  • image viewleading 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

构建并运行您的应用程序。 点击其中一位导演观看他们的电影。

3691932-fb127ea5c5172fe6.png

不错,但通过添加扩展单元格以显示有关每项作品的更多信息,将其提升到新的水平。 你的客户会喜欢这个!


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 PriorityContent Compression Resistance Priority更改为下图所示的值:

3691932-a38b386c44e96e46.png

Vertical Content Hugging Priority设置为252将有助于image view hug其内容,而不是在动画期间拉伸。将Vertical Compression Resistance Priority设置为1000可防止图像在其周围生长其他界面元素时被压缩。

构建并运行应用程序。选择一个导演并点击电影。您应该看到一些非常平滑的cell扩张,展示每部电影的信息。


Implementing Dynamic Type - 实现动态类型

您已向客户展示了自己的进步,他们非常喜欢它!但他们有一个最终要求。他们希望该应用程序支持更大的文本辅助功能。该应用程序需要根据客户的首选阅读尺寸进行调整。

动态类型(Dynamic Type)在iOS 7中引入,使这项任务变得简单。它使开发人员能够为不同的文本块(如标题或正文)指定不同的文本样式,并在用户更改设备设置中的首选大小时自动调整该文本。

回到Main.storyboardAuteurs场景。选择name label,然后在Attributes inspector中,完成以下步骤:

  • 1) 单击Font中的“T”图标。
  • 2) 从Text Styles下选择Headline
  • 3) 选中Automatically Adjusts Font
  • 4) 将Lines设置为0。
3691932-49df23f37c71a061.png

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并向右拖动滑块以将文本大小增加到较大的设置:

3691932-7a110f6eaaf850bc.png

然后,回到Auteurs应用程序'你的文字现在应该显得更大。 由于您在获取动态大小的单元格方面的工作,table view看起来仍然很棒:

3691932-f1c7572434ea5910.png

后记

本篇主要讲述了动态尺寸UITableViewCell的实现,感兴趣的给个赞或者关注~~~

3691932-b55270275f5ea911.png

猜你喜欢

转载自blog.csdn.net/weixin_34161029/article/details/87637682