如何在表视图中的UITableViewCells中使用“自动布局”,让每个单元格的内容和子视图确定行高度(自身/自动),同时保持平滑的滚动性能?
当前回答
swift 4
@IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!
@IBOutlet weak var tableView: UITableView!
private var context = 1
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.addObserver(self, forKeyPath: "contentSize", options: [.new,.prior], context: &context)
}
// Added observer to adjust tableview height based on the content
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if context == &self.context{
if let size = change?[NSKeyValueChangeKey.newKey] as? CGSize{
print("-----")
print(size.height)
tableViewHeightConstraint.constant = size.height + 50
}
}
}
//Remove observer
deinit {
NotificationCenter.default.removeObserver(self)
}
其他回答
只要你的单元格布局良好。
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
return [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
}
更新:您应该使用iOS 8中引入的动态调整大小。
如果单元格高度根据内容是动态的,则应精确计算出它,然后在渲染单元格之前返回高度值。一种简单的方法是在表视图单元格代码中定义计数方法,以便控制器在表单元格高度委托方法处调用。如果高度取决于表格或屏幕的宽度,请不要忘记计算实际单元格框架宽度(默认值为320)。也就是说,在表单元格高度委托方法中,首先使用cell.frame来校正单元格宽度,然后调用单元格中定义的计数高度方法来获得合适的值并返回它。
PS。可以在另一个方法中定义用于生成单元格对象的代码,以便调用不同的表视图单元格委托方法。
如果你有一根长长的绳子。例如没有换行符的。然后你可能会遇到一些问题。
公认的答案和其他几个答案都提到了“所谓的”修复。你只需要添加
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。
以防人们仍然对此感到困扰。我写了一篇关于在UITableViews中使用Autolayout的快速博客文章,利用Autolayout For Dynamic Cell Heights以及一个开源组件来帮助实现这一点,使其更加抽象和易于实现。https://github.com/Raizlabs/RZCellSizeManager
如果您以编程方式进行布局,以下是在Swift中使用锚的iOS 10需要考虑的问题。
有三个规则/步骤
数字1:在viewDidLoad上设置tableview的这两个财产,第一个属性告诉表视图应该期望其单元格的动态大小,第二个属性只是让应用程序计算滚动条指示器的大小,这样有助于提高性能。
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 100
第二:这一点很重要。您需要将子视图添加到单元格的contentView中,而不是添加到视图中,还需要使用其layoutsmargingguide将子视图锚定到顶部和底部,这是一个很好的示例。
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setUpViews()
}
private func setUpViews() {
contentView.addSubview(movieImageView)
contentView.addSubview(descriptionLabel)
let marginGuide = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
movieImageView.heightAnchor.constraint(equalToConstant: 80),
movieImageView.widthAnchor.constraint(equalToConstant: 80),
movieImageView.leftAnchor.constraint(equalTo: marginGuide.leftAnchor),
movieImageView.topAnchor.constraint(equalTo: marginGuide.topAnchor, constant: 20),
descriptionLabel.leftAnchor.constraint(equalTo: movieImageView.rightAnchor, constant: 15),
descriptionLabel.rightAnchor.constraint(equalTo: marginGuide.rightAnchor),
descriptionLabel.bottomAnchor.constraint(equalTo: marginGuide.bottomAnchor, constant: -15),
descriptionLabel.topAnchor.constraint(equalTo: movieImageView.topAnchor)
])
}
创建一个将添加子视图并执行布局的方法,在init方法中调用它。
第3:不要调用方法:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
}
如果你这样做,你将覆盖你的实现。
对于表视图中的动态单元格,请遵循以下3条规则。
这是一个有效的实现https://github.com/jamesrochabrun/MinimalViewController
推荐文章
- Objective-C中方法混合的危险是什么?
- 如何使用接口生成器创建的nib文件加载UIView
- iOS如何设置应用程序图标和启动图像
- 更改UITextField和UITextView光标/插入符颜色
- 'Project Name'是通过优化编译的——步进可能会表现得很奇怪;变量可能不可用
- 如何设置回退按钮文本在Swift
- 模拟器慢动作动画现在打开了吗?
- 如何为TableView创建NSIndexPath
- 滑动删除和“更多”按钮(就像iOS 7的邮件应用程序)
- 使UINavigationBar透明
- 如何改变推和弹出动画在一个基于导航的应用程序
- 删除/重置核心数据中的所有条目?
- setNeedsLayout vs. setNeedsUpdateConstraints和layoutIfNeeded vs. updateConstraintsIfNeeded
- 不区分大小写的比较
- 我怎么能得到一个uiimage的高度和宽度?