Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 348 Vote(s) - 3.49 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Resizing UITableView to fit content

#1
I am creating an app which will have a question in a `UILabel` and a multiple choice answers displayed in `UITableView`, each row showing a multiple choice. Questions and answers will vary, so I need this `UITableView` to be dynamic in height.

I would like to find a `sizeToFit` work around for the table. Where the table's frame is set to the height of all it's content.

Can anyone advise on how I can achieve this?
Reply

#2

I've tried this in iOS 7 and it worked for me

- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView sizeToFit];
}
Reply

#3
Add an observer for the contentSize property on the table view, and adjust the frame size accordingly

[your_tableview addObserver:self forKeyPath:@"contentSize" options:0 context:NULL];
then in the callback:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
CGRect frame = your_tableview.frame;
frame.size = your_tableview.contentSize;
your_tableview.frame = frame;
}

Hope this will help you.
Reply

#4
[Mimo's answer][1] and [Anooj VM 's answer][2] both are awesome but there is a small problem if you have a large list, it's possible that the height of the frame will cutoff some of your cells.

So. I have modified the answer a little bit:

<!-- language: lang-objC -->

dispatch_async(dispatch_get_main_queue()) {
//This code will run in the main thread:
CGFloat newHeight=self.tableView.contentSize.height;
CGFloat screenHeightPermissible=(self.view.bounds.size.height-self.tableView.frame.origin.y);
if (newHeight>screenHeightPermissible)
{
//so that table view remains scrollable when 'newHeight' exceeds the screen bounds
newHeight=screenHeightPermissible;
}

CGRect frame = self.tableView.frame;
frame.size.height = newHeight;
self.tableView.frame = frame;
}



[1]:

[To see links please register here]

[2]:

[To see links please register here]

Reply

#5
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;
});
Reply

#6
As an extension of Anooj VM's answer, I suggest the following to **refresh content size only when it changes.**

This approach also **disable scrolling** properly and **support larger lists** and **rotation**. There is no need to dispatch_async because contentSize changes are dispatched on main thread.

- (void)viewDidLoad {
[super viewDidLoad];
[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:NULL];
}


- (void)resizeTableAccordingToContentSize:(CGSize)newContentSize {
CGRect superviewTableFrame = self.tableView.superview.bounds;
CGRect tableFrame = self.tableView.frame;
BOOL shouldScroll = newContentSize.height > superviewTableFrame.size.height;
tableFrame.size = shouldScroll ? superviewTableFrame.size : newContentSize;
[UIView animateWithDuration:0.3
delay:0
options:UIViewAnimationOptionCurveLinear
animations:^{
self.tableView.frame = tableFrame;
} completion: nil];
self.tableView.scrollEnabled = shouldScroll;
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([change[NSKeyValueChangeKindKey] unsignedIntValue] == NSKeyValueChangeSetting &&
[keyPath isEqualToString:@"contentSize"] &&
!CGSizeEqualToSize([change[NSKeyValueChangeOldKey] CGSizeValue], [change[NSKeyValueChangeNewKey] CGSizeValue])) {
[self resizeTableAccordingToContentSize:[change[NSKeyValueChangeNewKey] CGSizeValue]];
}
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
[self resizeTableAccordingToContentSize:self.tableView.contentSize]; }

- (void)dealloc {
[self.tableView removeObserver:self forKeyPath:@"contentSize"];
}
Reply

#7
In case you don't want to track table view's content size changes yourself, you might find this subclass useful.

protocol ContentFittingTableViewDelegate: UITableViewDelegate {
func tableViewDidUpdateContentSize(_ tableView: UITableView)
}

class ContentFittingTableView: UITableView {

override var contentSize: CGSize {
didSet {
if !constraints.isEmpty {
invalidateIntrinsicContentSize()
} else {
sizeToFit()
}

if contentSize != oldValue {
if let delegate = delegate as? ContentFittingTableViewDelegate {
delegate.tableViewDidUpdateContentSize(self)
}
}
}
}

override var intrinsicContentSize: CGSize {
return contentSize
}

override func sizeThatFits(_ size: CGSize) -> CGSize {
return contentSize
}
}
Reply

#8
There is a much better way to do it if you use AutoLayout: change the constraint that determines the height. Just calculate the height of your table contents, then find the constraint and change it. Here's an example (assuming that the constraint that determines your table's height is actually a height constraint with relation "Equal"):

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

for constraint in tableView.constraints {
if constraint.firstItem as? UITableView == tableView {
if constraint.firstAttribute == .height {
constraint.constant = tableView.contentSize.height
}
}
}
}
Reply

#9
**Mu solution for this in swift 3**: Call this method in `viewDidAppear`


func UITableView_Auto_Height(_ t : UITableView)
{
var frame: CGRect = t.frame;
frame.size.height = t.contentSize.height;
t.frame = frame;
}
Reply

#10
**Swift 3, iOS 10.3**

***Solution 1:***
Just put `self.tableview.sizeToFit()` in `cellForRowAt indexPath` function. Make sure to set tableview height higher then you need.
This is a good solution if you don't have views below tableview. However, if you have, bottom tableview constraint will not be updated (I didn't try to fix it because I came up with solution 2)

Example:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as? TestCell {
cell.configureCell(data: testArray[indexPath.row])
self.postsTableView.sizeToFit()
return cell
}

return UITableViewCell()
}

***Solution 2:***
Set tableview height constraint in storyboard and drag it to the ViewController. If you know the average height of your cell and you know how many elements your array contains, you can do something like this:

tableViewHeightConstraint.constant = CGFloat(testArray.count) * 90.0 // Let's say 90 is the average cell height

***EDIT:**

After all the solutions I tried and every of them was fixing something, but not completely, [this][1] is the answer that explains and fixes this problem completely.


[1]:

[To see links please register here]

Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through