如何在表视图中的UITableViewCells中使用“自动布局”,让每个单元格的内容和子视图确定行高度(自身/自动),同时保持平滑的滚动性能?
当前回答
如果你有一根长长的绳子。例如没有换行符的。然后你可能会遇到一些问题。
公认的答案和其他几个答案都提到了“所谓的”修复。你只需要添加
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。
其他回答
@smileyborg提出的解决方案几乎是完美的。如果您有一个自定义单元格,并且需要一个或多个具有动态高度的UILabel,则systemLayoutSizeFittingSize方法与启用自动布局相结合将返回一个CGSizeZero,除非您将所有单元格约束从单元格移到其contentView(如@TomSwift在这里建议的那样,如何调整超视图的大小以适应具有自动布局的所有子视图?)。
为此,您需要在自定义UITableViewCell实现中插入以下代码(感谢@Adrian)。
- (void)awakeFromNib{
[super awakeFromNib];
for (NSLayoutConstraint *cellConstraint in self.constraints) {
[self removeConstraint:cellConstraint];
id firstItem = cellConstraint.firstItem == self ? self.contentView : cellConstraint.firstItem;
id seccondItem = cellConstraint.secondItem == self ? self.contentView : cellConstraint.secondItem;
NSLayoutConstraint *contentViewConstraint =
[NSLayoutConstraint constraintWithItem:firstItem
attribute:cellConstraint.firstAttribute
relatedBy:cellConstraint.relation
toItem:seccondItem
attribute:cellConstraint.secondAttribute
multiplier:cellConstraint.multiplier
constant:cellConstraint.constant];
[self.contentView addConstraint:contentViewConstraint];
}
}
将@smileyborg答案与此混合应该会奏效。
(对于底部读取的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那样。
以下是我的解决方案:
在TableView加载视图之前,您需要告诉它估计的高度。否则它将无法像预期的那样运行。
目标-C
- (void)viewWillAppear:(BOOL)animated {
_messageField.delegate = self;
_tableView.estimatedRowHeight = 65.0;
_tableView.rowHeight = UITableViewAutomaticDimension;
}
更新至Swift 4.2
override func viewWillAppear(_ animated: Bool) {
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 65.0
}
只要你的单元格布局良好。
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
return [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
}
更新:您应该使用iOS 8中引入的动态调整大小。
以防人们仍然对此感到困扰。我写了一篇关于在UITableViews中使用Autolayout的快速博客文章,利用Autolayout For Dynamic Cell Heights以及一个开源组件来帮助实现这一点,使其更加抽象和易于实现。https://github.com/Raizlabs/RZCellSizeManager
推荐文章
- 如何更改导航栏上“后退”按钮的标题
- 我如何获得iOS 7默认的蓝色编程?
- UITapGestureRecognizer破坏UITableView didSelectRowAtIndexPath
- 在Objective-C中@property保留,赋值,复制,非原子
- 6.5英寸屏幕的App store截图大小是多少?
- 我如何在NSAttributedString中创建一个可点击的链接?
- iOS测试/规格TDD/BDD以及集成和验收测试
- 停止UIWebView垂直“弹跳”?
- 启动屏幕故事板不显示图像
- 是否可以为iPhone应用程序(如YouTube和地图)注册一个基于http+域的URL方案?
- 模拟器错误fbssystemservicdomain代码4
- 改变UISegmentedControl的字体大小
- 我可以强制UITableView隐藏分隔符之间的空单元格吗?
- 获取用户当前位置/坐标
- 获得推送通知,而应用程序在前台iOS