Can You Get a Uitableview's Intrinsic Content Size to Update Based on the Number of Rows Shown If Scrolling Is Disabled

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 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?

Resizing UITableView to fit content

Actually I found the answer myself.

I just create a new CGRect for the tableView.frame with the height of table.contentSize.height

That sets the height of the UITableView to the height of its content.
Since the code modifies the UI, do not forget to run it in the main thread:

dispatch_async(dispatch_get_main_queue(), ^{
//This code will run in the main thread:
CGRect frame = self.tableView.frame;
frame.size.height = self.tableView.contentSize.height;
self.tableView.frame = frame;
});

iOS - How to make an UICollectionViewCell adapt its height according to its content ? (containing an UITableView)

Maybe it's not the answer you wanted, but here're my two cents. For your particular requirements, the better solution is moving away from UITableView, and use UIStackView or your custom container view.

Here's why:

  1. UITableView is a subclass of UIScrollView, but since you've disabled its scrolling feature, you don't need a UIScrollView.
  2. UITableView is mainly used to reuse cells, to improve performance and make code more structured. But since you're making it as large as its content size, none of your cells are reused, so features of UITableView is not taken any advantage of.

Thus, actually you don't need and you should not use either UITableView or UIScrollView inside the UICollectionViewCell for your requirements.

If you agree with above part, here're some learnings from our practices:

  1. We always move most of the underlying views and code logics, mainly data assembling, into a UIView based custom view, instead of putting in UITableViewCell or UICollectionViewCell directly. Then add it to UITableViewCell or UICollectionViewCell's contentView and setup constraints. With this structure, we can reuse our custom view in more scenarios.
  2. For requirements similar to yours, we'll create a factory class to create "rows" similar to how you create "cells" for your UITableView, add them into a vertical UIStackView, create constraints deciding UIStackView's width. Auto layout will take care of the rest things.
  3. In your usage with UICollectionViewCell, to calculate the wanted height, inside preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) func of your cell, you can use contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel) to calculate the height, do some check and return. Also, remember to invalidate layout when the width of the UICollectionView changes.

UITableView dynamic cell heights only correct after some scrolling

I don't know this is clearly documented or not, but adding [cell layoutIfNeeded] before returning cell solves your problem.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"TestCell"];
NSUInteger n1 = firstLabelWordCount[indexPath.row];
NSUInteger n2 = secondLabelWordCount[indexPath.row];
[cell setNumberOfWordsForFirstLabel:n1 secondLabel:n2];

[cell layoutIfNeeded]; // <- added

return cell;
}

UITableView inside UICollectionViewCell: expand to full size

Try using this -

class ContentSizedTableView: UITableView {
override var contentSize: CGSize {
didSet{
self.invalidateIntrinsicContentSize()
}
}

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

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

How do I size a UITextView to its content?

This works for both iOS 6.1 and iOS 7:

- (void)textViewDidChange:(UITextView *)textView
{
CGFloat fixedWidth = textView.frame.size.width;
CGSize newSize = [textView sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)];
CGRect newFrame = textView.frame;
newFrame.size = CGSizeMake(fmaxf(newSize.width, fixedWidth), newSize.height);
textView.frame = newFrame;
}

Or in Swift (Works with Swift 4.1 in iOS 11)

let fixedWidth = textView.frame.size.width
let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
textView.frame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)

If you want support for iOS 6.1 then you should also:

textview.scrollEnabled = NO;


Related Topics



Leave a reply



Submit