I have create a custom view MyIntrincView
which calculates its height automatically when setting its content. This works fine both in simulator and InterfaceBuilder.
However, when placing MyIntrinsicView
inside a UITableViewCell
, the cell height is not calculated correctly. Instead of automatically adopting the cell height to the intrinsic height of the view, all cell keep the same, initial height.
// A simple, custom view with intrinsic height. The height depends on
// the value of someProperty. When the property is changed setNeedsLayout
// is set and the height changes automatically.
@IBDesignable class MyIntrinsicView: UIView {
@IBInspectable public var someProperty: Int = 5 {
didSet { setNeedsLayout() }
}
override func layoutSubviews() {
super.layoutSubviews()
calcContent()
}
func calcContent() {
height = CGFloat(20 * someProperty)
invalidateIntrinsicContentSize()
}
@IBInspectable var height: CGFloat = 50
override var intrinsicContentSize: CGSize {
return CGSize(width: super.intrinsicContentSize.width, height: height)
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
invalidateIntrinsicContentSize()
}
}
// A simple cell which only contains a MyIntrinsicView subview. The view
// is attached to trailing, leading, top and bottom anchors of the cell.
// Thus the cell height should automatically match the height of the
// MyIntrinsicView
class MyIntrinsicCell: UITableViewCell {
@IBOutlet private var myIntrinsicView: MyIntrinsicView!
var someProperty: Int {
get { return myIntrinsicView.someProperty }
set {
myIntrinsicView.someProperty = newValue
// Cell DOES NOT rezise without manualle calling layoutSubviews()
myIntrinsicView.layoutSubviews()
}
}
}
...
// Simple tableView delegate which should create cells of different heights
// by giving each cell a different someProperty
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "IntrinsicCell", for: indexPath) as? MyIntrinsicCell ?? MyIntrinsicCell()
// Give all cell a different intrinsic height by setting someProperty to rowIndex
cell.someProperty = indexPath.row
return cell
}
I would expect, that each cell has a different height (20 * someProperty
= 20 * indexPath.row
). However, instead all cell have the same, initial height.
Only when explicitly calling myIntrinsicView.layoutSubviews()
the cells are created with the correct height.
It seems that the tableView does not call myIntrinsicView.layoutSubviews()
. Why is this?
When using a UILabel
instad of MyIntrinsicView
as cell content with different text lengths, everything works as expected. Thus the overall tableView setup is correct (= cell sizes are calculated automatically) and there has to be way to use intrinsic sizes correctly in UITableView
as well. So, what exactly is the correct way to do this?
As with your previous question here I think you're not really understanding what
intrinsicContentSize
does and does not do.When setting the text of a
UILabel
, yes, itsintrinsicContentSize
changes, but that's not all that happens.You also don't want to do what you're trying inside
layoutSubviews()
... if your code does trigger a change, you'll get into an infinite recursion loop (again, as with your previous question).Take a look at modifications to your code:
Two side notes...
1 - Your posted code shows you calling
myIntrinsicView.layoutSubviews()
... from Apple's docs:2 - For the direction it looks like you're headed, you would probably be better off manipulating constraint constants, rather than intrinsic content size.