在UITableView中使用自动布局进行动态单元布局&可变行高

如何在表视图中使用UITableViewCells中的自动布局,让每个单元格的内容和子视图确定行高(自身/自动),同时保持平滑的滚动性能

TL;博士:你不喜欢读书吗?直接跳到GitHub上的示例项目:

  • iOS 8示例项目-需要iOS 8
  • iOS 7示例项目-适用于iOS 7+

概念描述

下面的前两个步骤适用于您开发的iOS版本

一,。成立及;添加约束条件

UITableViewCell子类中,添加约束,使单元格的子视图的边固定到单元格的内容视图的边上(最重要的是固定到顶部和底部边缘)注意:不要将子视图固定到单元格本身;仅适用于单元格的内容视图通过确保每个子视图的垂直维度中的内容压缩阻力内容拥抱约束未被您添加的更高优先级约束覆盖,让这些子视图的固有内容大小驱动表视图单元格内容视图的高度。(哈?点击这里。)

记住,这个想法是让单元格的子视图垂直连接到单元格的内容视图,这样它们就可以;施加压力”;并使内容视图展开以适合它们。使用带有几个子视图的示例单元格,下面是一个直观的示例,说明了约束的某些部分(不是全部!)需要的外观:

您可以想象,当更多的文本添加到上面示例单元格中的多行主体标签时,它将需要垂直增长以适应文本,这将有效地迫使单元格增加高度。(当然,要使其正常工作,您需要获得正确的约束!)

在使用自动布局获得动态单元高度时,获得正确的约束肯定是最难也是最重要的部分。如果你在这里犯了一个错误,它可能会阻止其他一切工作——所以慢慢来!我建议您在代码中设置约束,因为您确切地知道哪些约束被添加到哪里,并且在出现问题时调试起来更容易。在代码中添加约束与使用布局锚的Interface Builder或GitHub上提供的一种出色的开源API一样简单,而且功能也要强大得多

  • 如果要在代码中添加约束,应该在UITableViewCell子类的updateConstraints方法中执行一次。请注意,updateConstraints可能会被多次调用,因此为了避免多次添加相同的约束,请确保在检查布尔属性(如didSetupConstraints时,将约束添加代码包装在updateConstraints中(在运行约束添加代码一次后,将其设置为“是”)。另一方面,如果您有更新现有约束的代码(例如调整某些约束的常量属性),将其放置在updateConstraints中,但不在检查didSetupConstraints的范围内,以便每次调用该方法时都可以运行它

2.确定唯一的表视图单元重用标识符

对于单元中的每个唯一约束集,使用唯一的单元重用标识符。换句话说,如果单元具有多个唯一布局,则每个唯一布局应接收其自己的重用标识符。(当您的单元变体具有不同数量的子视图,或者子视图以不同的方式排列时,您需要使用新的重用标识符,这是一个很好的提示。)

例如,如果您在每个单元格中显示电子邮件,则可能有4种独特的布局:仅包含主题的邮件、包含主题和正文的邮件、包含主题和照片附件的邮件,以及包含主题、正文和照片附件的邮件。每个布局都有实现此目标所需的完全不同的约束,因此一旦单元初始化并为其中一种单元类型添加了约束,该单元应获得特定于该单元类型的唯一重用标识符。这意味着当您将单元出列以进行重用时,该单元类型的约束已被添加并准备就绪

请注意,由于内部内容大小的差异,具有相同约束(类型)的单元格可能仍然具有不同的高度!不要因为内容大小不同而将根本不同的布局(不同的约束)与不同的计算图幅(从相同的约束解决)混淆

  • 不要将具有完全不同约束集的单元格添加到相同的重用池(即使用相同的重用标识符)然后尝试删除旧约束,并在每次出列后从头开始设置新约束。内部自动布局引擎的设计不是为了处理约束的大规模更改,您将看到大量性能问题

对于iOS 8-自动调整单元格大小

3.启用行高估计

若要启用自调整表视图单元格大小,必须设置表视图的
UITableViewAutomaticDimension的rowHeight属性。您还必须
为EstimateDrowEight属性指定一个值。只要两个
如果设置了这些属性,系统将使用自动布局来计算
行的实际高度

Apple:使用自调整大小的表格视图单元格

在iOS 8中,苹果已经将iOS 8之前必须由您执行的大部分工作内部化。为了允许自调整单元格大小机制工作,您必须首先将表视图上的rowHeight属性设置为常量UITableView.automaticDimension。然后,只需启用行高即可t通过将表视图的estimateDroweight属性设置为非零值来进行估计,例如:

self.tableView.rowHeight=UITableView.automaticDimension;
self.tableView.estimatedRowHeight=44.0;//设置为“平均”单元格高度

这样做的目的是为表视图提供尚未显示在屏幕上的单元格的行高的临时估计值/占位符。然后,当这些单元格即将在屏幕上滚动时,将计算实际行高。要确定每行的实际高度,表视图会自动询问每个单元格的cont高度entView需要基于内容视图的已知固定宽度(基于表视图的宽度,减去任何附加内容,如节索引或附件视图)以及已添加到单元格内容视图和子视图的自动布局约束。一旦确定了此实际单元格高度,将使用新的实际高度更新行的旧估计高度(并根据需要调整表视图的contentSize/contentOffset)

一般来说,您提供的估计值不一定非常准确——它仅用于在表视图中正确调整滚动指示器的大小,并且当您在屏幕上滚动单元格时,表视图在调整滚动指示器的错误估计值方面做得很好。您应该在可将视图(在viewdiload或类似视图中)设置为“平均”行高的常量值。仅当您的行高具有极端可变性时(例如,相差一个数量级)当您滚动时,您会注意到滚动指示器“跳跃”,您应该费心实现tableView:estimatedheightforrowatinexpath:,以执行所需的最小计算,从而为每行返回更准确的估计值。

对于iOS 7支持(自行实现自动单元大小调整)

3.进行布局传递并获取单元格高度

首先,实例化一个表视图单元格的屏幕外实例,每个重用标识符一个实例,该实例严格用于高度计算。(屏幕外意味着单元格引用存储在视图控制器上的属性/ivar中,并且从未从tableView:CellForRowatineXpath:返回,以便表视图实际呈现在屏幕上。)接下来,必须使用确切的内容(例如文本、图像等)配置单元格如果要在表视图中显示,它将保持不变

然后,强制单元格立即布局其子视图,然后使用UITableViewCellcontentView上的systemlayoutsfittingsize:方法找出单元格所需的高度。使用UILayoutFittingCompressedSize获得适合单元格所有内容所需的最小大小单元格。然后可以从tableView:heightforrowatinexpath:delegate方法返回高度

4.使用估计的行高

如果您的表视图中有几十行以上,您会发现在第一次加载表视图时,执行自动布局约束求解会很快使主线程陷入困境,因为在第一次加载时会对每一行调用tableView:heightforrowatinexpath:(以计算滚动指示器的大小)

从iOS 7开始,您可以(而且绝对应该)在表视图上使用estimatedRowHeight属性。这样做的目的是为表视图提供尚未在屏幕上显示的单元格的行高的临时估计/占位符。然后,当这些单元格即将在屏幕上滚动时,将计算实际行高(通过调用tableView:heightForRowAtIndexPath:),估计高度将更新为实际高度

一般来说,您提供的估计值不一定非常精确——它只用于在表视图中正确调整滚动指示器的大小,而表视图在调整滚动指示器方面做得很好

发表评论