Uitableviewcell with Intrinsic Height Based on Width

UITableViewCell with intrinsic height based on width

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)
}

How to create an UITableView with an intrinsic Height in Swift 5 using UIKit?

You can use something along the lines of:

final class AutoSizingTableView: UITableView {

override init(frame: CGRect, style: UITableView.Style) {
super.init(frame: frame, style: style)
setContentCompressionResistancePriority(.required, for: .vertical)
}

required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setContentCompressionResistancePriority(.required, for: .vertical)
}

override var contentSize: CGSize {
didSet {
invalidateIntrinsicContentSize()
}
}

override func reloadData() {
super.reloadData()
invalidateIntrinsicContentSize()
}

override var intrinsicContentSize: CGSize {
setNeedsLayout()
layoutIfNeeded()
return contentSize
}
}

Basically what this does is set an intrinsicContentSize equal to the contentSize when the data of the tableview is reloaded, or if the contentSize changes.

However you have to ask yourself the question: If I don't need dequeuing logic, do I actually need a tableview?

Can you get a UITableView's intrinsic content size to update based on the number of rows shown if scrolling is disabled?

Ok, so unlike UITextView, it doesn't look like UITableView ever returns an intrinsic size based on the visible rows. But that's not that big a deal to implement via a subclass, especially if there's a single section, no headers or footers, and the rows are of a fixed height.

class AutoSizingUiTableView : UITableView
{
override func intrinsicContentSize() -> CGSize
{
let requiredHeight = rowCount * rowHeight
return CGSize(width: UIView.noIntrinsicMetric, height: CGFloat(requiredHeight))
}
}

I'll leave it up to the reader to figure out how to get their own rowCount. The same if you have variable heights, multiple sections, etc. You just need more logic.

By doing this, it works great with AutoLayout. I just wish it handled this automatically.

How can I adjust the UITableViewCell height to the content of UITextView that's inside?

The key to getting self-sizing table cells (autolayout-based, which I recommend) is as follows:

  • Add your subviews to the contentView of the UITableViewCell
  • Provide constraints between your subviews and the contentView such that your subviews reach all edges of the table cell. In your case, this probably means aligning the leading, trailing, top, and bottom edges of your UITextView to the corresponding edges of the contentView.
  • Set the row height to UITableViewAutomaticDimension instead of a hardcoded CGFloat.
  • Somewhere in your controller, provide an estimation of the height with tableView.estimatedRowHeight = x (a hard coded constant is fine, this is for performance).


Related Topics



Leave a reply



Submit