所以我制作了自定义视图ExpressionView来可视化数学表达式。表达式的每个部分都是UILabel,包含一个数字或操作,并且标签在ExpressionView中以右对齐的行对齐。我希望ExpressionView有从外部自动布局约束定义的宽度,但高度应该是内在的,这取决于我将不得不为给定的宽度制作多少行标签。
对齐方法效果很好,我使用frame。每个标签的大小和ExpressionView的宽度,进行计算,设置标签的框架位置,并计算包含所有行所需的高度。我还定义了这些,以便设置固有的大小,并在每次ExpressionLabel的宽度改变时重新布局标签:
override var intrinsicContentSize: CGSize {
let res = CGSize(width: UIView.noIntrinsicMetric,
height: CGFloat(self.contentHeight) //counted during self.layoutAllLabels()
)
return res
}
public override func layoutSubviews() {
super.layoutSubviews() //finish layout to get current label sizes and self width
let preHeight = self.contentHeight
self.layoutAllLabels() //layout all labels in current width, count required height
//if required height changed, relayout everything
if (preHeight != self.contentHeight) {
self.invalidateIntrinsicContentSize()
self.superview?.setNeedsLayout()
self.superview?.layoutIfNeeded()
}
}
在正常的uiview中,当ExpressionLabel通过自动布局约束设置到位时,一切都工作得很好。但是当ExpressionLabel被固定在UITableViewCell的contentView中时,它会失败,我希望它能定义细胞的高度。我的UITableView有自动单元格大小所需的这些行
self.estimatedRowHeight = 50
self.rowHeight = UITableView.automaticDimension
单个单元格的大小以某种方式发生并且通常是正确的,但通常也给出无效的高度。它可能与单元重用有关(通过dequeueReusableCells()),因为旧的高度可能保留在重用的单元中。但是为什么他们不更新,当他们应该在任何ExpressionView.layoutSubviews()调用更新?单元格在UITableView中创建时不自动布局吗?如果不是,在哪里做我的标签布局,以便它将正确更新,即使在UITableView?当我知道最终单元格宽度,并能调整其子视图的固有高度根据它?
明白了!似乎自维UITableViewCell的高度是由UITableView通过调用细胞的systemLayoutSizeFitting()
来确定的。在我的单元格类中,我已经重写了该方法,并在调用super. systemlayoutsizefiting()之前调用layoutIfNeeded() -以便具有正确的尺寸。这很有效!也许我甚至可以直接调用ExpressionView的安排方法,而不是通过layoutIfNeeded(),但我会让它保持原样。
这是我的单元格类中的重写:
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
//force layout of all subviews including ExpressionView, which
//updates ExpressionView's intrinsic height, and thus height of a cell
self.setNeedsLayout()
self.layoutIfNeeded()
//now intrinsic height is correct, so I can call super method
return super.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: horizontalFittingPriority, verticalFittingPriority: verticalFittingPriority)
}