使用多个Stack View的Self sizing cell无法自适应高度如何解决?
无法自适应高度的核心原因及修复方案
核心问题点
- 约束添加位置错误:你将所有布局约束写在
layoutSubviews方法中,该方法会在cell生命周期内多次触发,每次都会添加重复约束,不仅会引发约束冲突,更关键的是约束添加时机晚于tableView第一次计算cell高度的时机,系统找不到高度约束时会使用默认44高度,生成你看到的UIView-Encapsulated-Layout-Height == 44的系统约束,后续你再添加自定义高度约束就会直接冲突。 - 纯代码cell的配置错误:你使用纯代码创建cell,
awakeFromNib方法永远不会被触发,你写在该方法内的自适应高度相关配置完全无效。 - 缺失必要约束:你只给左右栈内的imageView设置了100x100的尺寸,但是没有给左右两个垂直stackView添加宽度约束,水平主stackView的三个子视图宽度无法确定,会导致中间多行label的换行计算错误,进而高度计算异常。
- 内容更新后未触发高度重算:异步加载图片、赋值文本后没有通知tableView重新计算对应cell的高度,tableView会沿用之前计算的旧高度。
修复步骤
- 把所有约束代码从
layoutSubviews方法中删除,全部挪到init(style:reuseIdentifier:)方法末尾执行,保证约束只添加一次,且在cell初始化时就完成配置:
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier:reuseIdentifier) stackViewLeft.addArrangedSubview(leftImageView) stackViewLeft.addArrangedSubview(leftLabel) // 给左栈设置元素间距 stackViewLeft.spacing = 4 stackViewRight.addArrangedSubview(rightImageView) stackViewRight.addArrangedSubview(rightLabel) // 给右栈设置元素间距 stackViewRight.spacing = 4 stackViewMain.addArrangedSubview(stackViewLeft) stackViewMain.addArrangedSubview(middleLabel) stackViewMain.addArrangedSubview(stackViewRight) // 新增:给左右栈添加固定宽度约束 stackViewLeft.widthAnchor.constraint(equalToConstant: 100).isActive = true stackViewRight.widthAnchor.constraint(equalToConstant: 100).isActive = true // 直接给imageView加宽高约束,无需加到父栈上,同时设置图片显示规则避免溢出 leftImageView.widthAnchor.constraint(equalToConstant: 100).isActive = true leftImageView.heightAnchor.constraint(equalToConstant: 100).isActive = true leftImageView.contentMode = .scaleAspectFill leftImageView.clipsToBounds = true rightImageView.widthAnchor.constraint(equalToConstant: 100).isActive = true rightImageView.heightAnchor.constraint(equalToConstant: 100).isActive = true rightImageView.contentMode = .scaleAspectFill rightImageView.clipsToBounds = true contentView.addSubview(stackViewMain) // 直接在初始化方法中激活主栈约束 NSLayoutConstraint.activate([ stackViewMain.topAnchor.constraint(equalTo: contentView.topAnchor,constant: 0), stackViewMain.leadingAnchor.constraint(equalTo: contentView.leadingAnchor,constant: 0), stackViewMain.trailingAnchor.constraint(equalTo: contentView.trailingAnchor,constant: 0), stackViewMain.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0) ]) }
- 删掉整个
layoutSubviews的重写方法,不需要再使用。 - 删掉
awakeFromNib方法,纯代码创建的cell不会触发该回调,内部代码完全无效。 - 如果你的图片是异步网络加载,需要在图片加载完成的回调中,通知当前tableView重算对应行的高度,示例如下:
// 先在cell中定义图片加载完成回调 var didLoadImage: (() -> Void)? // 在图片加载完成的闭包中触发回调 leftImageView.loadImageFromURL(url: profileUrl, completion: { [weak self] in self?.didLoadImage?() }) // 在tableView的cellForRow方法中监听回调,触发高度重算 cell.didLoadImage = { [weak self] in self?.tableView.beginUpdates() self?.tableView.endUpdates() }
- 确认tableView的代理方法没有实现
heightForRowAt返回固定值,如果实现了该方法,你之前设置的UITableView.automaticDimension会直接失效。
内容的提问来源于stack exchange,提问作者EK_AllDay
相关产品推荐
相关产品推荐

