Change UITableView height dynamically
Solution 1
There isn't a system feature to change the height of the table based upon the contents of the tableview. Having said that, it is possible to programmatically change the height of the tableview based upon the contents, specifically based upon the contentSize
of the tableview (which is easier than manually calculating the height yourself). A few of the particulars vary depending upon whether you're using the new autolayout that's part of iOS 6, or not.
But assuming you're configuring your table view's underlying model in viewDidLoad
, if you want to then adjust the height of the tableview, you can do this in viewDidAppear
:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self adjustHeightOfTableview];
}
Likewise, if you ever perform a reloadData
(or otherwise add or remove rows) for a tableview, you'd want to make sure that you also manually call adjustHeightOfTableView
there, too, e.g.:
- (IBAction)onPressButton:(id)sender
{
[self buildModel];
[self.tableView reloadData];
[self adjustHeightOfTableview];
}
So the question is what should our adjustHeightOfTableview
do. Unfortunately, this is a function of whether you use the iOS 6 autolayout or not. You can determine if you have autolayout turned on by opening your storyboard or NIB and go to the "File Inspector" (e.g. press option+command+1 or click on that first tab on the panel on the right):
Let's assume for a second that autolayout was off. In that case, it's quite simple and adjustHeightOfTableview
would just adjust the frame
of the tableview:
- (void)adjustHeightOfTableview
{
CGFloat height = self.tableView.contentSize.height;
CGFloat maxHeight = self.tableView.superview.frame.size.height - self.tableView.frame.origin.y;
// if the height of the content is greater than the maxHeight of
// total space on the screen, limit the height to the size of the
// superview.
if (height > maxHeight)
height = maxHeight;
// now set the frame accordingly
[UIView animateWithDuration:0.25 animations:^{
CGRect frame = self.tableView.frame;
frame.size.height = height;
self.tableView.frame = frame;
// if you have other controls that should be resized/moved to accommodate
// the resized tableview, do that here, too
}];
}
If your autolayout was on, though, adjustHeightOfTableview
would adjust a height constraint for your tableview:
- (void)adjustHeightOfTableview
{
CGFloat height = self.tableView.contentSize.height;
CGFloat maxHeight = self.tableView.superview.frame.size.height - self.tableView.frame.origin.y;
// if the height of the content is greater than the maxHeight of
// total space on the screen, limit the height to the size of the
// superview.
if (height > maxHeight)
height = maxHeight;
// now set the height constraint accordingly
[UIView animateWithDuration:0.25 animations:^{
self.tableViewHeightConstraint.constant = height;
[self.view setNeedsUpdateConstraints];
}];
}
For this latter constraint-based solution to work with autolayout, we must take care of a few things first:
Make sure your tableview has a height constraint by clicking on the center button in the group of buttons here and then choose to add the height constraint:
Then add an
IBOutlet
for that constraint:Make sure you adjust other constraints so they don't conflict if you adjust the size tableview programmatically. In my example, the tableview had a trailing space constraint that locked it to the bottom of the screen, so I had to adjust that constraint so that rather than being locked at a particular size, it could be greater or equal to a value, and with a lower priority, so that the height and top of the tableview would rule the day:
What you do here with other constraints will depend entirely upon what other controls you have on your screen below the tableview. As always, dealing with constraints is a little awkward, but it definitely works, though the specifics in your situation depend entirely upon what else you have on the scene. But hopefully you get the idea. Bottom line, with autolayout, make sure to adjust your other constraints (if any) to be flexible to account for the changing tableview height.
As you can see, it's much easier to programmatically adjust the height of a tableview if you're not using autolayout, but in case you are, I present both alternatives.
Solution 2
create your cell by xib or storyboard. give it's outlet's contents.
now call it in CellForRowAtIndexPath.
eg. if you want to set cell height according to Comment's label text.
so set you commentsLbl.numberOfLine=0;
so set you commentsLbl.numberOfLine=0;
then in ViewDidLoad
self.table.estimatedRowHeight = 44.0 ;
self.table.rowHeight = UITableViewAutomaticDimension;
and now
-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewAutomaticDimension;}
Solution 3
Lots of the answers here don't honor changes of the table or are way too complicated. Using a subclass of UITableView
that will properly set intrinsicContentSize
is a far easier solution when using autolayout. No height constraints etc. needed.
class UIDynamicTableView: UITableView
{
override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
return CGSize(width: UIViewNoIntrinsicMetric, height: self.contentSize.height)
}
override func reloadData() {
super.reloadData()
self.invalidateIntrinsicContentSize()
}
}
Set the class of your TableView to UIDynamicTableView
in the interface builder and watch the magic as this TableView will change it's size after a call to reloadData()
.
Solution 4
This can be massively simplified with just 1 line of code in viewDidAppear:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
tableViewHeightConstraint.constant = tableView.contentSize.height
}
Solution 5
Rob's solution is very nice, only thing that in his -(void)adjustHeightOfTableview
method the calling of
[self.view needsUpdateConstraints]
does nothing, it just returns a flag, instead calling
[self.view setNeedsUpdateConstraints]
will make the desired effect.
alex
Updated on April 06, 2020Comments
-
alex about 4 years
I want to change the height of my tableview from another viewcontroller based on the sum of its cells' heights, as they are dynamic. Is it at all possible? Thanks
Add-on:
What I basically have is a UserProfileViewController that has a containerview on half of the screen. There I add different other viewcontrollers:
In the case of the wall button this is how I add the viewcontroller and it's subsequent tableview:
- (IBAction)wallButtonPressed:(id)sender { //Check if there is an instance of the viewcontroller we want to display. If not make one and set it's tableview frame to the container's view bounds if(!_userWallViewController) { self.userWallViewController = [[WallViewController alloc] init]; // self.userWallViewController.activityFeedTableView.frame = self.containerView.bounds; } [self.userWallViewController.containerView addSubview:self.userWallViewController.activityFeedTableView]; //If the currentviewcontroller adn it's view are already added to the hierarchy remove them [self.currentViewController.view removeFromSuperview]; [self.currentViewController removeFromParentViewController]; //Add the desired viewcontroller to the currentviewcontroller self.currentViewController = self.userWallViewController; //Pass the data needed for the desired viewcontroller to it's instances self.userWallViewController.searchURLString = [NSString stringWithFormat:@"event/user/%@/", self.userID]; self.userWallViewController.sendCommentURLString = [NSString stringWithFormat:@"event/message/%@", self.userID]; [self.userWallViewController.activityFeedTableView reloadData]; self.userWallViewController.totalCellHeight = ^(float totalCellHeight){ self.scrollView.contentSize = CGSizeMake(320.0, totalCellHeight); CGRect newFrame = self.userWallViewController.containerView.frame; newFrame.size.height = totalCellHeight + 33.0; self.userWallViewController.containerView.frame = newFrame; self.userWallViewController.activityFeedTableView.frame = self.containerView.bounds; }; //Add this containerview to the desired viewcontroller's containerView self.userWallViewController.containerView = self.containerView; //Add the needed viewcontroller and view to the parent viewcontroller and the containerview [self addChildViewController:self.userWallViewController]; [self.containerView addSubview:self.userWallViewController.view]; //CLEAN UP THE CONTAINER VIEW BY REMOVING THE PREVIOUS ADDED TABLE VIEWS [self.userFansViewController.userSimpleTableView removeFromSuperview]; [self.fanOfViewController.userSimpleTableView removeFromSuperview]; [self.userPublishedMovellaListViewController.gridView removeFromSuperview]; [self.userPublishedMovellaListViewController removeFromParentViewController]; self.userPublishedMovellaListViewController = nil; }
and in that viewcontroller this is where I initialize my tableview:
-(UITableView *)activityFeedTableView { if (!_activityFeedTableView) { _activityFeedTableView = [[UITableView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 850.0) style:UITableViewStylePlain]; } return _activityFeedTableView; }
I am calculating he total sum of the cell's height, the problem is that the cell's height method is called way after the getter of te tableview is called. So I would need some sort of way to know when the cells' height method is done for all cells and then I can resize my tableview. Thanks
-
alex over 11 yearswow. that must be the best structured answer I've ever received. Much appreciate that. The thing is my app also supports ios 5 so I can't really use that feature even though I'm glad I've learned something out of your answer. I have updated my question. Can you please take a look. Thank you
-
Rob over 11 years@alex Well, the answer is all the same, but just without all of that constraint silliness. Just use the first rendition of the
adjustHeightOfTableView
and you should be good. -
Dejell over 10 yearsI tried you answer for autolayout off but unfortunately my table view is not displayed at all :(
-
Tiago Martins over 10 years@Dejel: Check that you're doing the [self adjustHeightOfTableView] in viewDidAppear and not in viewDidLoad.
-
Leon Storey over 10 yearsOnly just upgrading our apps to AutoLayout (dropping support for iOS5) and the new
NSLayoutConstraint
is, once setup, a very nice alternative to setting frames. -
wiznaibus about 10 yearsI cannot upvote this enough. Well done on this answer.
-
Alex about 10 yearsOne additional question to your answer: I have one table view that has a dynamic number of cells. I'm using the above method to modify the height constraint and it works fine. I would like to make an improvement though and that is to show the table view only after the height constraint has been modified (therefore not in viewDidAppear, where you can see the height being changed). Any ideas on that? I need this to work on iOS 7.0 and 7.1 (with Auto Layout, of course)
-
Rob about 10 years@Alex You could presumably hide it before resizing (either by changing the default
frame
oralpha
). Or try doing it inviewWillLayoutSubviews
. -
Alex about 10 years@Rob I tried to do it in
viewWillLayoutSubviews
already, but I'm getting an error. Made a question about it here stackoverflow.com/questions/23464303/… Any idea how to make that work? -
Allen about 10 years@Rob thanks for including the autolayout version. would have been stuck without that :)
-
Tony J Stark almost 10 years@Rob What an answer! Thanks, you saved me a lot of work.
-
Ricky almost 10 yearsI have some difficulties in making the right TableView constraints inside the ScrollView with AutoLayout, adjusting the constraint height programatically solved the problem!
-
westmark over 9 years@Rob Your answer (especially Auto Layout version) saved me from going insane :)
-
Sakthimuthiah almost 9 yearsThanks, it got worked. After implementing this answer, i had added " override func viewDidAppear(animated: Bool) { tableView.reloadData() } " this code from the following link "appcoda.com/self-sizing-cells" - Then i got resized row in cell
-
János almost 9 yearswhat about
UITextView
there is nonumberOfLine
property -
Fa.Shapouri almost 9 years@János UITextView Adjust height automatically, If you fixed size, the scroll in textView going to be enabled.
-
ReyJenald over 8 yearsThanks. this saved me hours.
-
matt bezark over 7 yearsthis is good. to be more complete it could also handle the max value.
-
LimeRed over 7 yearsThanks. I would prefer setting a max value with a lessOrEqual height constraint. This way the class stays very simple, and you can set the maximum easy from the interface builder.
intrinsicContentSize
would be used unless it exceeds your lessOrEqual constraint. Then scrolling would be enabled. -
Badr Filali about 7 yearsYour code didn't work for me I got the error : " Argument labels '(_:, _:)' do not match to any available overloads
-
LimeRed about 7 yearsWas using a custom extension for CGSize initialization. Edited my answer to use the default initialization. Please try again ;)
-
Maninderjit Singh about 7 yearsAmazing job @Mohittomar
-
Rishab over 6 yearsNo idea why this is working for me. i thought I learned about view life cycles. Apparently not. Good find.
-
wildbagel over 6 yearsAwesome answer! I have been searching for this solution for days. Wish I could upvote 5 or 6 times.
-
Cœur almost 6 yearsPossible improvement:
height: self.contentSize.height + self.contentInset.top + self.contentInset.bottom
-
droid almost 3 yearsThis is a good answer if you don't have text or view below comment label