如何在表视图中的UITableViewCells中使用“自动布局”,让每个单元格的内容和子视图确定行高度(自身/自动),同时保持平滑的滚动性能?
当前回答
(对于底部读取的Xcode 8.x/Xcode 9.x)
注意Xcode 7.x中的以下问题,这可能是混淆的根源:
Interface Builder无法正确处理自动调整单元格大小的设置。即使你的约束绝对有效,IB仍然会抱怨,并给你令人困惑的建议和错误。原因是IB不愿意根据您的约束条件改变行的高度(以便单元格适合您的内容)。相反,它会保持行的高度不变,并开始建议您更改约束,您应该忽略这些约束。
例如,假设您已经设置好了一切,没有警告,没有错误,一切正常。
现在,如果您更改字体大小(在本例中,我将描述标签的字体大小从17.0更改为18.0)。
由于字体大小增加,标签现在希望占据3行(之前它占据2行)。
如果Interface Builder按预期工作,它将调整单元格的高度以适应新的标签高度。然而,实际发生的情况是IB显示红色的自动布局错误图标,并建议您修改拥抱/压缩优先级。
您应该忽略这些警告。相反,您可以*手动更改行的高度(选择“单元格”>“大小检查器”>“行高度”)。
我一次单击一次(使用上/下步进器)改变这个高度,直到红色箭头错误消失!(实际上,您会收到黄色警告,此时只需继续并执行“更新帧”,一切都应该正常)。
*请注意,您实际上不必在Interface Builder中解决这些红色错误或黄色警告-在运行时,一切都会正常工作(即使IB显示错误/警告)。只需确保在运行时控制台日志中没有出现任何自动布局错误。事实上,尝试总是更新IB中的行高度是非常令人讨厌的,有时几乎不可能(因为分数值)。为了防止烦人的IB警告/错误,您可以选择所涉及的视图,并在Size Inspector中为属性Ambiguity选择Verify Position Only
Xcode 8.x/Xcode 9.x看起来(有时)做的事情与Xcode 7.x不同,但仍然不正确。例如,即使抗压优先级/拥抱优先级设置为必需(1000),Interface Builder也可能拉伸或剪裁标签以适合单元格(而不是调整单元格高度以适合标签周围)。在这种情况下,它甚至可能不会显示任何自动布局警告或错误。或者,有时它完全像上面描述的Xcode7.x那样。
其他回答
假设您有一个带有子视图的单元格,并且希望该单元格的高度足够高,以包含子视图+填充。
1) 将子视图的底部约束设置为cell.contentView减去所需的填充。不要对单元格或cell.contentView本身设置约束。
2) 将tableView的rowHeight属性或tableView:heightForRowAtIndexPath:设置为UITableViewAutomaticDimension。
3) 将tableView的estimatedRowHeight属性或tableView:estimatedHeightForRowAtIndexPath:设置为高度的最佳猜测。
就是这样。
如果你有一根长长的绳子。例如没有换行符的。然后你可能会遇到一些问题。
公认的答案和其他几个答案都提到了“所谓的”修复。你只需要添加
cell.myCellLabel.preferredMaxLayoutWidth = tableView.bounds.width
我发现Suragh的回答最完整、最简洁,因此不会令人困惑。
虽然没有解释为什么需要这些改变。让我们这样做吧。
将以下代码放到项目中。
import UIKit
class ViewController: UIViewController {
lazy var label : UILabel = {
let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.backgroundColor = .red
lbl.textColor = .black
return lbl
}()
override func viewDidLoad() {
super.viewDidLoad()
// step0: (0.0, 0.0)
print("empty Text intrinsicContentSize: \(label.intrinsicContentSize)")
// ----------
// step1: (29.0, 20.5)
label.text = "hiiiii"
print("hiiiii intrinsicContentSize: \(label.intrinsicContentSize)")
// ----------
// step2: (328.0, 20.5)
label.text = "translatesAutoresizingMaskIntoConstraints"
print("1 translate intrinsicContentSize: \(label.intrinsicContentSize)")
// ----------
// step3: (992.0, 20.5)
label.text = "translatesAutoresizingMaskIntoConstraints translatesAutoresizingMaskIntoConstraints translatesAutoresizingMaskIntoConstraints"
print("3 translate intrinsicContentSize: \(label.intrinsicContentSize)")
// ----------
// step4: (328.0, 20.5)
label.text = "translatesAutoresizingMaskIntoConstraints\ntranslatesAutoresizingMaskIntoConstraints\ntranslatesAutoresizingMaskIntoConstraints"
print("3 translate w/ line breaks (but the line breaks get ignored, because numberOfLines is defaulted to `1` and it will force it all to fit into one line! intrinsicContentSize: \(label.intrinsicContentSize)")
// ----------
// step5: (328.0, 61.0)
label.numberOfLines = 0
print("3 translate w/ line breaks and '0' numberOfLines intrinsicContentSize: \(label.intrinsicContentSize)")
// ----------
// step6: (98.5, 243.5)
label.preferredMaxLayoutWidth = 100
print("3 translate w/ line breaks | '0' numberOfLines | preferredMaxLayoutWidth: 100 intrinsicContentSize: \(label.intrinsicContentSize)")
setupLayout()
}
func setupLayout(){
view.addSubview(label)
label.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
}
注意,我没有添加任何大小限制。我只添加了centerX和centerY约束。但标签的大小仍然正确为什么?
因为contentSize。
为了更好地处理这个问题,首先保留步骤0,然后注释掉步骤1-6。让setupLayout()保持不变。观察行为。
然后取消注释步骤1,并观察。
然后取消注释步骤2并观察。
执行此操作,直到取消注释所有6个步骤并观察它们的行为。
从这一切可以得出什么结论?什么因素可以改变contentSize?
文本长度:如果文本较长,则intrinsicContentSize的宽度将增加换行符:如果添加,则intrinsicContentSize的宽度将是所有行的最大宽度。如果一行有25个字符,另一行有2个字符,而另一行则有21个字符,则将根据25个字符计算宽度允许的行数:必须将numberOfLines设置为0,否则将不会有多行。numberOfLines将调整intrinsicContentSize的高度进行调整:假设基于文本,intrinsicContentSize的宽度为200,高度为100,但您希望将宽度限制为标签的容器,您打算做什么?解决方案是将其设置为所需的宽度。通过将preferredMaxLayoutWidth设置为130,则新的intrinsicContentSize的宽度大约为130。高度显然会超过100,因为你需要更多的线条。也就是说,如果你的约束设置正确,那么你根本不需要使用这个!有关更多信息,请参阅此答案及其评论。如果没有限制宽度/高度的约束,则只需要使用preferredMaxLayoutWidth,例如“除非文本超过preferredMaxLayoutWidth否则不要换行”。但如果你将前导/尾随和numberOfLines设置为0,那么你就可以100%确定了!长话短说这里推荐使用它的大多数答案都是错误的!你不需要它。需要它意味着你的约束没有正确设置,或者你只是没有约束字体大小:还请注意,如果增加fontSize,则intrinsicContentSize的高度将增加。我的代码中没有显示这一点。你可以自己试试。
回到tableViewCell示例:
您需要做的是:
将numberOfLines设置为0将标签正确约束到边距/边无需设置preferredMaxLayoutWidth。
TL;DR:不喜欢读书吗?直接跳转到GitHub上的示例项目:
iOS 8示例项目-需要iOS 8iOS 7示例项目-适用于iOS 7+
概念性描述
无论您正在为哪个iOS版本开发,下面的前两个步骤都适用。
1.设置和添加约束
在UITableViewCell子类中,添加约束,使单元格的子视图的边缘固定到单元格的contentView的边缘(最重要的是顶部和底部边缘)。注意:不要将子视图固定到单元格本身;仅限于单元格的contentView!通过确保每个子视图的垂直维度中的内容压缩阻力和内容拥抱约束不会被添加的更高优先级约束覆盖,让这些子视图的固有内容大小驱动表视图单元格内容视图的高度。(嗯?点击这里。)
记住,这样做的目的是使单元格的子视图垂直连接到单元格的内容视图,以便它们可以“施加压力”,并使内容视图扩展以适应它们。使用带有几个子视图的示例单元格,下面是一些(不是所有!)约束所需的外观的直观图示:
您可以想象,当更多的文本添加到上面示例单元格中的多行正文标签时,它需要垂直增长以适应文本,这将有效地迫使单元格高度增长。(当然,您需要正确设置约束条件,才能正确工作!)
在使用Auto Layout获得动态单元格高度的过程中,正确设置约束无疑是最困难也是最重要的部分。如果你在这里犯了一个错误,它可能会阻止其他一切工作——所以慢慢来吧!我建议您在代码中设置约束,因为您确切地知道哪些约束被添加到哪里,并且在出现问题时更容易调试。在代码中添加约束可能与使用布局锚或GitHub上提供的一个很棒的开源API的Interface Builder一样简单,也比它强大得多。
如果要在代码中添加约束,应该在UITableViewCell子类的updateConstraints方法中执行一次。请注意,updateConstraints可能被多次调用,因此为了避免多次添加相同的约束,请确保在检查诸如didSetupConstraints之类的布尔属性(在运行一次约束添加代码后将其设置为YES)时,将约束添加代码包装在updateConstraints中。另一方面,如果您有更新现有约束的代码(例如调整某些约束的常量属性),请将其放在updateConstraints中,但不要检查didSetupConstraints,以便每次调用方法时都可以运行。
2.确定唯一的表视图单元重用标识符
对于单元中的每个唯一约束集,使用唯一的单元重用标识符。换句话说,如果您的单元格有多个唯一的布局,那么每个唯一的布局都应该接收自己的重用标识符。(当单元变量具有不同数量的子视图,或者子视图以不同的方式排列时,需要使用新的重用标识符。)
例如,如果您在每个单元格中显示一封电子邮件,则可能有4种独特的布局:仅包含主题的邮件、包含主题和正文的邮件、带有主题和照片附件的邮件,以及包含主题、正文和图片附件的邮件。每个布局都有实现它所需的完全不同的约束,因此,一旦初始化了单元并为这些单元类型之一添加了约束,单元就应该获得特定于该单元类型的唯一重用标识符。这意味着当您将一个单元出列以供重用时,约束已经被添加,并准备好用于该单元类型。
请注意,由于固有内容大小的差异,具有相同约束(类型)的单元格可能仍然具有不同的高度!不要因为内容大小不同而将根本不同的布局(不同的约束)与不同的计算视图框架(根据相同的约束解决)混淆。
不要将具有完全不同约束集的单元格添加到同一个重用池(即使用相同的重用标识符),然后在每次出队后尝试删除旧约束并从头开始设置新约束。内部自动布局引擎的设计不是为了处理约束的大规模变化,您会看到大量的性能问题。
对于iOS 8-自动调整单元格大小
3.启用行高度估计
要启用自调整表视图单元格大小,必须设置表视图的rowHeight属性设置为UITableViewAutomaticDimension。您还必须为estimatedRowHeight属性赋值。一旦设置这些财产后,系统使用自动布局计算行的实际高度Apple:使用自调整表视图单元格
在iOS 8中,苹果已经将之前必须由您在iOS 8之前完成的大部分工作内部化。为了允许自动调整单元格大小机制工作,必须首先将表视图上的rowHeight属性设置为常量UITableView.automaticDimension。然后,只需通过将表视图的estimatedRowHeight属性设为非零值来启用行高度估计,例如:
self.tableView.rowHeight = UITableView.automaticDimension;
self.tableView.estimatedRowHeight = 44.0; // set to whatever your "average" cell height is
这将为表格视图提供尚未显示在屏幕上的单元格行高度的临时估计值/占位符。然后,当这些单元格即将在屏幕上滚动时,将计算实际的行高度。为了确定每行的实际高度,表视图会根据内容视图的已知固定宽度(该宽度基于表视图的宽度,减去任何其他内容(如分区索引或附件视图))以及添加到单元格内容视图和子视图的自动布局约束,自动询问每个单元格的contentView需要的高度。一旦确定了此实际单元格高度,将使用新的实际高度更新行的旧估计高度(并根据需要对表视图的contentSize/contentOffset进行任何调整)。
一般来说,您提供的估计值不必非常准确——它只用于在表格视图中正确调整滚动指示器的大小,而表格视图在您滚动屏幕上的单元格时,可以很好地调整滚动指示器以显示不正确的估计值。您应该将表视图(在viewDidLoad或类似视图中)的estimatedRowHeight属性设置为一个常量值,即“平均”行高度。只有当您的行高度具有极大的可变性(例如,相差一个数量级),并且您在滚动时注意到滚动指示器“跳跃”时,才需要麻烦地实现tableView:estimatedHeightForRowAtIndexPath:以进行所需的最小计算,从而为每行返回更准确的估计值。
对于iOS 7支持(自己实现自动调整单元格大小)
3.通过布局并获取单元格高度
首先,实例化一个表视图单元的屏幕外实例,每个重用标识符对应一个实例,该实例严格用于高度计算。(屏幕外意味着单元格引用存储在视图控制器上的属性/ivar中,并且从未从tableView:cellForRowAtIndexPath:返回,以使表视图在屏幕上实际呈现。)接下来,必须使用如果要在表视图中显示,它将保存的确切内容(例如文本、图像等)配置单元格。
然后,强制单元格立即布局其子视图,然后使用UITableViewCell的contentView上的systemLayoutSizeFittingSize:方法来确定单元格所需的高度。使用UILayoutFittingCompressedSize获取适合单元格所有内容所需的最小大小。然后可以从tableView:heightForRowAtIndexPath:delegate方法返回高度。
4.使用估计的行高度
如果您的表视图中有十几行以上的行,那么您会发现,在首次加载表视图时,执行自动布局约束解决会很快使主线程陷入停滞,因为在首次加载时,会对每一行调用tableView:heightForRowAtIndexPath:(以便计算滚动指示器的大小)。
从iOS 7开始,您可以(而且绝对应该)在表视图上使用estimatedRowHeight属性。这将为表格视图提供尚未显示在屏幕上的单元格行高度的临时估计值/占位符。然后,当这些单元格即将在屏幕上滚动时,将计算实际的行高度(通过调用tableView:heightForRowAtIndexPath:),并用实际高度更新估计的高度。
一般来说,您提供的估计值不必非常准确——它只用于在表格视图中正确调整滚动指示器的大小,而表格视图在您滚动屏幕上的单元格时,可以很好地调整滚动指示器以显示不正确的估计值。您应该将表视图(在viewDidLoad或类似视图中)的estimatedRowHeight属性设置为一个常量值,即“平均”行高度。只有当您的行高度具有极大的可变性(例如,相差一个数量级),并且您在滚动时注意到滚动指示器“跳跃”时,才需要麻烦地实现tableView:estimatedHeightForRowAtIndexPath:以进行所需的最小计算,从而为每行返回更准确的估计值。
5.(如果需要)添加行高缓存
如果您已经完成了以上所有操作,但仍然发现在tableView:heightForRowAtIndexPath:中执行约束求解时性能慢得令人无法接受,那么很遗憾,您需要为单元格高度实现一些缓存。(这是苹果工程师建议的方法。)总的想法是让自动布局引擎第一次解决约束,然后缓存该单元格的计算高度,并将缓存的值用于该单元格高度的所有未来请求。当然,诀窍是确保在发生任何可能导致单元格高度发生变化的情况时,清除单元格的缓存高度——主要是当该单元格的内容发生变化或发生其他重要事件时(如用户调整动态类型文本大小滑块)。
iOS 7通用示例代码(有很多有趣的评论)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Determine which reuse identifier should be used for the cell at this
// index path, depending on the particular layout required (you may have
// just one, or may have many).
NSString *reuseIdentifier = ...;
// Dequeue a cell for the reuse identifier.
// Note that this method will init and return a new cell if there isn't
// one available in the reuse pool, so either way after this line of
// code you will have a cell with the correct constraints ready to go.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
// Configure the cell with content for the given indexPath, for example:
// cell.textLabel.text = someTextForThisCell;
// ...
// Make sure the constraints have been set up for this cell, since it
// may have just been created from scratch. Use the following lines,
// assuming you are setting up constraints from within the cell's
// updateConstraints method:
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
// If you are using multi-line UILabels, don't forget that the
// preferredMaxLayoutWidth needs to be set correctly. Do it at this
// point if you are NOT doing it within the UITableViewCell subclass
// -[layoutSubviews] method. For example:
// cell.multiLineLabel.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds);
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Determine which reuse identifier should be used for the cell at this
// index path.
NSString *reuseIdentifier = ...;
// Use a dictionary of offscreen cells to get a cell for the reuse
// identifier, creating a cell and storing it in the dictionary if one
// hasn't already been added for the reuse identifier. WARNING: Don't
// call the table view's dequeueReusableCellWithIdentifier: method here
// because this will result in a memory leak as the cell is created but
// never returned from the tableView:cellForRowAtIndexPath: method!
UITableViewCell *cell = [self.offscreenCells objectForKey:reuseIdentifier];
if (!cell) {
cell = [[YourTableViewCellClass alloc] init];
[self.offscreenCells setObject:cell forKey:reuseIdentifier];
}
// Configure the cell with content for the given indexPath, for example:
// cell.textLabel.text = someTextForThisCell;
// ...
// Make sure the constraints have been set up for this cell, since it
// may have just been created from scratch. Use the following lines,
// assuming you are setting up constraints from within the cell's
// updateConstraints method:
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
// Set the width of the cell to match the width of the table view. This
// is important so that we'll get the correct cell height for different
// table view widths if the cell's height depends on its width (due to
// multi-line UILabels word wrapping, etc). We don't need to do this
// above in -[tableView:cellForRowAtIndexPath] because it happens
// automatically when the cell is used in the table view. Also note,
// the final width of the cell may not be the width of the table view in
// some cases, for example when a section index is displayed along
// the right side of the table view. You must account for the reduced
// cell width.
cell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds));
// Do the layout pass on the cell, which will calculate the frames for
// all the views based on the constraints. (Note that you must set the
// preferredMaxLayoutWidth on multiline UILabels inside the
// -[layoutSubviews] method of the UITableViewCell subclass, or do it
// manually at this point before the below 2 lines!)
[cell setNeedsLayout];
[cell layoutIfNeeded];
// Get the actual height required for the cell's contentView
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
// Add an extra point to the height to account for the cell separator,
// which is added between the bottom of the cell's contentView and the
// bottom of the table view cell.
height += 1.0;
return height;
}
// NOTE: Set the table view's estimatedRowHeight property instead of
// implementing the below method, UNLESS you have extreme variability in
// your row heights and you notice the scroll indicator "jumping"
// as you scroll.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Do the minimal calculations required to be able to return an
// estimated row height that's within an order of magnitude of the
// actual height. For example:
if ([self isTallCellAtIndexPath:indexPath]) {
return 350.0;
} else {
return 40.0;
}
}
示例项目
iOS 8示例项目-需要iOS 8iOS 7示例项目-适用于iOS 7+
由于表视图单元格包含UILabels中的动态内容,这些项目是具有可变行高度的表视图的完整工作示例。
Xamarin(C#/.NET)
如果您正在使用Xamarin,请查看@KentBoogaart整理的这个示例项目。
可变高度UITableViewCell的快速示例
为Swift 3更新
胡威廉(William Hu)的快速回答很好,但在我第一次学习做某事时,它能帮助我掌握一些简单而详细的步骤。下面的示例是我在学习使用可变单元格高度制作UITableView时的测试项目。我基于Swift的这个基本UITableView示例。
完成的项目应如下所示:
创建新项目
它可以只是一个单视图应用程序。
添加代码
将新的Swift文件添加到项目中。将其命名为MyCustomCell。此类将保存添加到情节提要中单元格的视图的出口。在这个基本示例中,每个单元格中只有一个标签。
import UIKit
class MyCustomCell: UITableViewCell {
@IBOutlet weak var myCellLabel: UILabel!
}
我们稍后将连接此插座。
打开ViewController.swift并确保您具有以下内容:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// These strings will be the data for the table view cells
let animals: [String] = [
"Ten horses: horse horse horse horse horse horse horse horse horse horse ",
"Three cows: cow, cow, cow",
"One camel: camel",
"Ninety-nine sheep: sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep baaaa sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep sheep",
"Thirty goats: goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat goat "]
// Don't forget to enter this in IB also
let cellReuseIdentifier = "cell"
@IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// delegate and data source
tableView.delegate = self
tableView.dataSource = self
// Along with auto layout, these are the keys for enabling variable cell height
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
}
// number of rows in table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.animals.count
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
cell.myCellLabel.text = self.animals[indexPath.row]
return cell
}
// method to run when table view cell is tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped cell number \(indexPath.row).")
}
}
重要说明:
以下两行代码(以及自动布局)使可变单元格高度成为可能:tableView.estimatedRowHeight=44.0tableView.rowHeight=UITableViewAutomaticDimension
设置情节提要
将表视图添加到视图控制器中,并使用自动布局将其固定到四边。然后将表视图单元格拖到表视图上。然后将标签拖到原型单元格上。使用自动布局将标签固定到表视图单元的内容视图的四个边缘。
重要说明:
自动布局与我上面提到的两行重要代码一起工作。如果不使用自动布局,它将无法工作。
其他IB设置
自定义类名和标识符
选择TableViewCell并将自定义类设置为MyCustomCell(我们添加的Swift文件中的类名称)。还将Identifier设置为cell(与上面代码中用于cellReuseIdentifier的字符串相同)。
标签的零行
将标签中的行数设置为0。这意味着多行,并允许标签根据其内容调整自身大小。
连接插座
控件从情节提要中的表视图拖动到ViewController代码中的tableView变量。对Prototype单元格中的Label和MyCustomCell类中的myCellLabel变量执行同样的操作。
完成了
您现在应该能够运行项目并获得高度可变的单元格。
笔记
此示例仅适用于iOS 8及更高版本。如果您仍然需要支持iOS 7,那么这将不适用于您。未来项目中您自己的自定义单元格可能不止一个标签。确保将所有内容固定正确,以便自动布局可以确定要使用的正确高度。你可能还需要使用垂直抗压和拥抱。有关这方面的更多信息,请参阅本文。如果不固定前缘和后缘(左侧和右侧),则可能还需要设置标签的preferredMaxLayoutWidth,以便它知道何时换行。例如,如果在上面的项目中向标签添加了“水平居中”约束,而不是固定前缘和后缘,则需要将此行添加到tableView:cellForRowAtIndexPath方法:cell.myCellLabel.referencedMaxLayoutWidth=表格视图边界宽度
另请参见
了解iOS 8中的自调整单元格和动态类型具有不同行高的表视图单元格Swift的UITableView示例
我必须使用动态视图(通过代码设置视图和约束),当我想设置preferredMaxLayoutWidth标签的宽度为0时。所以我有错误的细胞高度。
然后我添加了
[cell layoutSubviews];
执行之前
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
之后,标签的宽度符合预期,动态高度计算正确。
推荐文章
- iPhone上UIView和UILabels的渐变
- keychain上的分发证书中缺少私钥
- 在实现API时,我如何避免在块中捕获自我?
- 如何创建一个Swift Date对象?
- Xcode 4在目标设备上说“finished running <my app>”——什么都没有发生
- 从另一个应用程序打开设置应用程序
- 快速提取正则表达式匹配
- 如何应用梯度的背景视图的iOS Swift应用程序
- 图书馆吗?静态的?动态吗?或框架?另一个项目中的项目
- 如何用SwiftUI调整图像大小?
- Xcode 6 gitignore文件应该包括什么?
- 如何在iPhone/iOS上删除电话号码的蓝色样式?
- 检测视网膜显示
- 如何在UIImageView中动画图像的变化?
- 如何从iPhone访问SOAP服务