问题
So I have made custom view ExpressionView for visualizing mathematical expression. Every part of expression is UILabel holding one number or operation, and labels are aligned inside the ExpressionView in right-justified rows. I expect ExpressionView to have width defined by autolayout constraints from outside, but height should be intrinsic, depending on how many rows of labels I will have to make for a given width.
The aligning methods works well, I use frame.size of each label and width of ExpressionView, do the math, set frame positions of labels, and count required height to contain all rows. I have also defined these, in order to set intrinsic size, and relayout labels every time width of ExpressionLabel changes:
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()
}
}
Everything works well when ExpressionLabel is set in place by autolayout constraints in normal UIViews. BUT it fails when ExpressionLabel is pinned inside UITableViewCell's contentView, and I expect it to define cell's height. My UITableView has these lines required for automatic cell sizing
self.estimatedRowHeight = 50
self.rowHeight = UITableView.automaticDimension
The sizing of individual cells somehow takes place and is usually correct, but often also gives invalid heights. It might have something to do with cell reusing (through dequeueReusableCells()), because old heights may remain in reused cells. But why they are not updated, when they should update in any ExpressionView.layoutSubviews() call? Is the cell NOT automatically layouted when created in UITableView? If not, where to do layouting of my labels so that it will update correctly even in UITableView? When I know final cell width, and can adjust its subview's intrinsic height according to it?
回答1:
Got it! It seems that height of autodimensioned UITableViewCell is determined from UITableView by calling systemLayoutSizeFitting()
of the cell . In my cell class, I have overriden the method, and call layoutIfNeeded() before calling super.systemLayoutSizeFitting() - in order to have correct dimensions. And that works! Maybe I even could call the ExpressionView arranging method directly, not through layoutIfNeeded(), but I will leave it as it is.
This is the override in my cell class:
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)
}
来源:https://stackoverflow.com/questions/65775159/uitableviewcell-with-intrinsic-height-based-on-width