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:
UITableView
is a subclass ofUIScrollView
, but since you've disabled its scrolling feature, you don't need aUIScrollView
.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 ofUITableView
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:
- We always move most of the underlying views and code logics, mainly data assembling, into a
UIView
based custom view, instead of putting inUITableViewCell
orUICollectionViewCell
directly. Then add it toUITableViewCell
orUICollectionViewCell
'scontentView
and setup constraints. With this structure, we can reuse our custom view in more scenarios. - 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 verticalUIStackView
, create constraints decidingUIStackView
's width. Auto layout will take care of the rest things. - In your usage with
UICollectionViewCell
, to calculate the wanted height, insidepreferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes)
func of your cell, you can usecontentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel)
to calculate the height, do some check and return. Also, remember to invalidate layout when the width of theUICollectionView
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
Create Endless Cgpath Without Framedrops
Dynamic Datasource/Delegates for Uitableview in Swift
Firebase Storage Overwriting Files
Comparing Non-Optional Any to Nil Is Always False
How to Initialize a Mlmultiarray in Coreml
How to Catch Error When Setting Launchpath in Nstask
Why Upload Alamofire Background Request Don't Executes in Background
Convert [(Key: String, Value: String)] in [String:String]
How to Use View Controller (Calendarkit) in Swiftui Application
Xcode: Using Custom Fonts Inside Dynamic Framework
Swift 2 Mkmapviewdelegate Rendererforoverlay Optionality
Images Inaccessible from Asset Catalog in a Swiftui Framework
Cannot Invoke Initializer for Type 'Sqlite3_Destructor_Type'
Get the First Day of Week Without Weekcalendarunit
How to Extend Float3 or Any Other Built-In Type to Conform to the Codable Protocol