How to make UITableView's height dynamic with autolayout feature? - ios

I am using autolayout in Xcode 5.
I set the table view's height to Greater than or equal to 200px. I want that it has dynamic size. Because sometimes it will have many rows, sometimes it will have a few rows.
But the size is always 200px. And if the content is larger than that, I should scroll down to see the lower rows.
What should I do to give the tableview dynamic size?

This is tested with the latest version of Xcode.
1) In Xcode go to the storyboard and click on you table view to select it.
2) In the Utilities pane (see picture 1) make sure the constraint for the table view height is defined.
3) In the view controller that displays the table view create an outlet to this constraint:
In Swift 3
#IBOutlet weak var dynamicTVHeight: NSLayoutConstraint!
In Objective C
#property (strong, nonatomic) IBOutlet NSLayoutConstraint *dynamicTVHeight;
4) In the storyboard go back to the height constraint for the UITableView and verify that the icon in the right has changed the color from purple to blue (see picture 2)
4) Also put this code in the same view controller:
Swift
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let height = min(self.view.bounds.size.height, tableView.contentSize.height)
dynamicTVHeight.constant = height
self.view.layoutIfNeeded()
}
Objective C
- (void)viewWillAppear:(BOOL)animated
{
// just add this line to the end of this method or create it if it does not exist
[self.tableView reloadData];
}
-(void)viewDidLayoutSubviews
{
CGFloat height = MIN(self.view.bounds.size.height, self.tableView.contentSize.height);
self.dynamicTVHeight.constant = height;
[self.view layoutIfNeeded];
}
This should solve your problem.
These are the links to two versions of the sample project that does what you want, one for Objective C and the other one for Swift 3. Download it and test it with Xcode. Both projects work with the latest version of Xcode, Xcode 8.3.2.
https://github.com/jcatalan007/TestTableviewAutolayout
https://github.com/jcatalan007/TestTableviewAutolayoutSwift

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;
then in ViewDidLoad
self.table.estimatedRowHeight = 44.0 ;
self.table.rowHeight = UITableViewAutomaticDimension;
and now
-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewAutomaticDimension;}
voila ............ you did it....

It can be done programmatically. The concept (and code itself) is actually very simple:
In the updateConstraints method of myTableView's superview, add a constraint so that myTableView's height is equal to myTableView.contentSize.height.
Tested on Xcode 6 targeting iOS 7.

You can Ctrl+Drag the height constraint of your UITableView into your view controller source code and give it a name. Then in updateViewConstraints of your ViewController you can set the constant of that constraint to tableView.contentSize.height
....
#IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint?
....
override func updateViewConstraints() {
super.updateViewConstraints()
tableViewHeightConstraint?.constant = tableView.contentSize.height
}

Or you can satisfy height constraint from storyboard and check:
[x]Remove at build time
Then, in your controller, update tableview's height which contains all your cells stuff. (*I use PureLayout as framework over AutoLayout)

Related

iOS 8 : how to set height programmatically for a UITableview in a freeform ViewController

Rookie in iOS.
I have a Freeform ViewController, which has an UIImageView and
UITableView in it.
The Problem i am facing here is when i try to scroll the tableview
some of the items stay hidden because of the height it has taken in the freeform
viewcontroller.
I tried the following code to set the UItableview's height programmatically
according to the size of the screen.
let screenSize: CGRect = UIScreen.mainScreen().bounds
tableview.frame = CGRectMake(0,imageview.frame.height, 280, (screenSize.height -imageview.frame.height))
The above code failed to resize the tableview.
Is there any other way that i can set the UITableview size at Runtime or
in the storyboard.
Solution
I resolved the issue after laid out my both views using custom height and width after specifying translatesAutoresizingMaskIntoConstraints = true for both the views. I did as per my design and app requirement.
if you are using auto layout or constraints, you have to
tableview.translatesAutoresizingMaskIntoConstraints = true
before applying the new frame, the new frame is not updating because it's getting over-riden by auto layout and constraints.
EDIT :
1- if you have [adjust scroll view insets] enabled in your view controller in story boards disable it, and test and see.
2- do you create the imageView programatically? or from interface builder, and do you have constraints on it?
3- did you know that the status bar has a height and you have to compensate for it too?
4- do you use a navigation bar?
There is a delegate function for the UITableViewCell height.
Here you specify the indexPath of that particular cell and return your height for it
(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.section == yourSection && indexPath.row == yourRow) {
return 140.0; //return height for required row
}
// "Else"
return someDefaultHeight;
}
Don't set Tableview height programmatically.
Just do one thing
Take Top, Bottom, Leading, Trailing constraints and set constant value to 0
Add user image view into tableview header.

Update constraints of other elements when UITableView height change

I have a UITableView of which I am defining height as 200 in autolayout. Based on that I am laying out other elements below it like UITextField etc. After that in run time I am fetching data from server and populating in UITableView due to which i am updating UITableView's height based on its content size. Following code I am using for it
self.myTableView.frame = CGRectMake(0 , 0, self.myTableView.frame.width, self.myTableView.contentSize.height)
But due to this, all the elements placed below UITableView still appear at same location which they were while laying out in Autolayout. Means change in height of UITableView makes no difference to them. Following image depicts this problem. What could be possible solution for this?
Here you can see, text fields are getting overlapped on tableview at run time. I am using Swift 2 in Xcode 7.2
If you have all required constraints to your table view and other view.
Don't change the frame of TableView to change height of it.
Instead create IBOutlet of height constraint of your tablview.
e.g. say IBOutlet name is constraintTableViewHeight,
then you can the the hight easily.
constraintTableViewHeight.constant = yourNewHeightValue
//update all constraint of your view and its inner view
self.view.layoutIfNeeded();
Refer Image to create IBOutlet for your height Constraint.
Take IBOutlet of NSLayoutConstraint for tableView Height and set its value not set tableview frame it's not working if you are already given constraints for tableview so change tableview hight constant
like if you take tblHeight for tableView height then set tblHeight.constant = self.myTableView.contentSize.height
You have to create an outlet connection for your table view height constraint:
#IBOutlet var heightConstraint: NSLayoutConstraint!
and when you want to change table view height, you can change the constraint value like this:
heightConstraint.constant = newHeightValue
self.view.layoutIfNeeded()

Dynamically Sized UITextView in Storyboard

I would like to add a paragraph of text to a UIView in my storyboard in the form of a non-editable, non-scrolling and non-selectable UITextView. I would like the UITextView to take up a variable number of lines based on how many it needs. It should have a fixed width and y position. How do I accomplish this in the storyboard in such a way that I can link other UI elements' constraints to the bottom of the UITextView?
Make a constraint pinning the text view’s height to some constant, and make an IBOutlet to that constraint so you can access it in code. Whenever the text changes, set the constraint’s constant to the text view’s contentSize.height:
textView.sizeToFit()
textView.layoutIfNeeded()
textViewHeightConstraint.constant = textView.sizeThatFits(CGSize(textView.frame.size.width, CGFloat.max)).height
Editing Zev Eisenberg answer for Swift 3. CGFloat.max doesn't work anymore. There is now CGFloat.greatestFiniteMagnitude
So updated version for Swift 3 is:
#IBOutlet var textViewHeight: NSLayoutConstraint!
textView.sizeToFit()
textView.layoutIfNeeded()
textViewHeight.constant = textView.sizeThatFits(CGSize(width: textView.frame.size.width,
height: CGFloat.greatestFiniteMagnitude)).height
Selected answer is not work for me. Then i found my solution;
1- Uncheck Scrolling Enabled property. Then, make an IBOutlet that constraint and write that code;
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
textViewConstraint.constant = textView.intrinsicContentSize().height
}

AutoLayout Dynamic .xib View Height

I've not been able to find much in the way of AutoLayout with individual .xib files...
I've got a standalone .xib file that has 3 views - a header view (which contains two labels), an input, and a footer (which contains two buttons). It looks like this:
The labels in the header view have constraints which should affect the vertical size of the header view, and in turn the size of the entire view. The subheader is a label with 0 lines, which means it is multi-line and dynamic. Everything else has a set height with horizontal constraints to superview and top constraints to sibling (or superview in header view's case).
The issue I am having is that when I load this .xib file in code for display, the height is always static based on what is defined in Xcode's inspectors. Is it possible to make the height of the entire view dynamic based on the width (which affects dynamic label height and therefore rest of the view)?
For example - if I load this view from the .xib and set its width to 300, how do I then have it resize its height to accommodate the dynamic label's new height? Do I need to use the intrinsicContentSize method to define this size?
After much experimentation and reading, I have found the answer. When loading the .xib in some sort of constructor (in my case a class level convenience method), you must make sure to call [view setTranslatesAutoresizingMaskIntoConstraints:NO]; For example, I've done the following:
+ (InputView *)inputViewWithHeader:(NSString *)header subHeader:(NSString *)subHeader inputValidation:(ValidationBlock)validation
{
InputView *inputView = [[[NSBundle mainBundle] loadNibNamed:#"InputView" owner:self options:nil] lastObject];
if ([inputView isKindOfClass:[InputView class]]) {
[inputView setTranslatesAutoresizingMaskIntoConstraints:NO];
[inputView configureWithHeader:header subHeader:subHeader inputValidation:validation];
[inputView layoutIfNeeded];
[inputView invalidateIntrinsicContentSize];
return inputView;
}
return nil;
}
Then, it's necessary to override layoutSubviews and intrinsicContentSize. Overriding layoutSubviews allows me to set the preferredMaxLayoutWidth of my label, while overriding intrinsicContentSize allows me to calculate the size based on constraints and subviews! Here is my implementation of those:
- (void)layoutSubviews {
[super layoutSubviews];
self.subHeaderLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds);
[super layoutSubviews];
}
- (CGSize)intrinsicContentSize {
CGFloat height = self.headerView.bounds.size.height;
height += self.headerInputSpacer.constant;
height += self.inputField.bounds.size.height;
height += self.inputButtonSpacer.constant;
height += self.buttonView.bounds.size.height;
CGSize size = CGSizeMake([UIScreen mainScreen].bounds.size.width - 20, height);
return size;
}
I'm sure there are ways to improve this, or better ways to make it happen, but for now it is at least sized correctly! Very useful for views that should not have user-defined frames.
This is my approach (Swift version):
Embed everything inside a view. Your xib hierarchy should be:
Your xib
View
Everything else, your labels, buttons etc.
View's constraint should constraint to Top, Leading and Trailing to superview(your xib), and inside your "everything else", there should be an object's bottom constraint to the superview (View).
In your code:
Load the nib
nib.frame.size.width = 80 // Set your desired width here
nib.label.text = "hello world" // Set your dynamic text here
nib.layoutIfNeeded() // This will calculate and set the heights accordingly
nib.frame.size.height = nib.view.frame.height + 16 // 16 is the total of top gap and bottom gap of auto layout
For my case, I'm loading this xib into collection view's cell, the xib's height is to be dynamically adjusted, and the width is to follow cell's width. Lastly, the above code block is inside my cellForItemAt method of the collection view.
Hope it helps.
Unfortunately, I am not sure what I was missing. The above methods don't work for me to get the xib cell's height or let the layoutifneeded(), UITableView.automaticDimension to do the height calculation. I've been searching and trying for 3 to 4 nights but without an answer.
Some answers here or on another post do give me hints for the workaround though. It's a stupid method but it works. Just add all your cells into an Array. And then set the outlet of each of your height constraint in the xib storyboard. Finally, add them up in the heightForRowAt method. It's just straight forward if you are not familiar with the those APIs.
Swift 4.2
CustomCell.Swift
#IBOutlet weak var textViewOneHeight: NSLayoutConstraint!
#IBOutlet weak var textViewTwoHeight: NSLayoutConstraint!
#IBOutlet weak var textViewThreeHeight: NSLayoutConstraint!
#IBOutlet weak var textViewFourHeight: NSLayoutConstraint!
#IBOutlet weak var textViewFiveHeight: NSLayoutConstraint!
MyTableViewVC.Swift
.
.
var myCustomCells:[CustomCell] = []
.
.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Bundle.main.loadNibNamed("CustomCell", owner: self, options: nil)?.first as! CustomCell
.
.
myCustomCells.append(cell)
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let totalHeight = myCustomCells[indexPath.row].textViewOneHeight.constant + myCustomCells[indexPath.row].textViewTwoHeight.constant + myCustomCells[indexPath.row].textViewThreeHeight.constant + myCustomCells[indexPath.row].textViewFourHeight.constant + myCustomCells[indexPath.row].textViewFiveHeight.constant
return totalHeight + 40 //some magic number
}

Dynamic Table Height according to number of cell in table

I am developing app that puts the data in UILable that are parsed from Json, and there are details, which should be in table. I am using Xcode 4.4.1, and targeted for iOS 4.3 and above.
The table height has been developed dynamic where the height of table varies according to the height if cell height is changed on the run time. I question is, now how can I able to change the table height according to the number of cell present in that table so that user wont have to scroll separately in the table.
try this,
TableView.frame=CGRectMake(10, 10, 200 ,MIN(40 * [YourArray count], 600));
assuming 40 is the height of your cell, and minimum height for your tableview is 600.
Krunal solution will work fine if all the cells have the same height. However, if you have dynamic cell heights you have to follow these steps:
1- Set the height constraint for the table from the storyboard.
2- Drag the height constraint from the storyboard and create #IBOutlet for it in the viewController file.
#IBOutlet weak var tableViewHeightConstraint: NSLayoutConstraint!
3- Then you can change the height for the table dynamicaly using this code:
override func viewWillLayoutSubviews() {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
super.updateViewConstraints()
self.tableViewHeightConstraint?.constant = self.tableView.contentSize.height
})
}

Resources