How to change UITableViewCell's height dynamically using Auto Layout? - ios

A self-sizing UITableViewCell: MyTableViewCell.m
-(void) viewDidLoad{
UIView *sample = [[UIView alloc] init];
[self.contentView addSubview: sample];
//Fake code here. Let sample view fill the whole contentView:
Constraint1: sample.leading = contentView.leading;
Constraint2: sample.trailing = contentView.trailing;
Constraint3: sample.top = contentView.top;
Constraint4: sample.bottom = contentView.bottom;
Constraint5: sample.height = 20;
NSLayoutConstraint:activateConstriant(constraints 1-5);
}
This code works and the MyTableViewCell has a height of 20.
However, I want to change the height at run time (after this cell added to a UITableView), so I add a new method to MyTableViewCell.m
-(void) updateHeight:(CGFloat)newHeight{
//Fake code here. change the constant of constraint1
Constraint1 = get reference of Constraint1 in viewDidLoad();
Constraint1.constant = newHeight
}
I run the code and call updateHeight:10 and got the log warning:
2017-03-16 16:23:11.102858 [14541:568021] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"",
"",
"",
""
)
Will attempt to recover by breaking constraint
My own opinion is that my constraints created at viewDidLoad automatically creates couple of other constraints like contentView.height=20 and my change to the original constraints conflict with the constraints created by system.
I tested to change the leading of sample view instead of the height in updateHeight:, and it works! Because the change of leading doesn't affect contentView's own height constraint?
How am I supposed to do a dynamically change to auto layout like this case?
Thank you~~~

To have dynamic cell size you need to implement the below methods
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return <your estimated cell height>;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
}

Set the tableview row height in your view controller. Refer the following code line :
self.tableView.rowHeight = UITableViewAutomaticDimension;

Make sure your updateHeight: method executed before cell's layoutSubviews. Because if cells has been loaded, you have to reload the tableView to make your new constraints effective, so you can figure out your automatic constraints in tableView:cellForRowAtIndexPath: method, or the following code is what you need.
[tableView beginUpdates];
// your code, alter your cell's constraints here
[tableView endUpdates];

tableView.estimatedRowHeight = 44.0 //put as per your cell row height
tableView.rowHeight = UITableViewAutomaticDimension
Give Top,Bottom,Leading,Trailing Contraints to UILabel used.
Be sure numberOfLine for your UILabel in the cell is set to 0.
Estimated row height needs to be provided.(Provide any appropriate value)

I ran into this issue today and solved it by overriding the UITableViewCell object's drawRect method.
- (void) drawRect:(CGRect)rect {
[super drawRect:rect];
[self adjustConstraint];
}

Related

iOS table view cell auto height with custom subview

I need pretty simple thing - table cells which automatically detect their own height. There are a lot of examples out there, which basically say "set couple of flags and values on your table and use autolayout in your cell". My case is a bit special - I load the content of the cell from a separate XIB file which is a plain UIView. The first problem comes already when I create that XIB file in interface builder - I don't know how to make it's height to "calculate" according to constraints that I specify. I am always getting IB errors unless I switch to "Freeform" and resize the view to the height matching my constraints. If I don't do that, I will get the following errors:
In the above example I just need a view which height will be 32 (image height) + 2 * 16 (vertical distance constraints of image from parent's top and bottom) plus the margins. Resizing the freeform view in IB until the errors disappear seems like a dirty hack to me, and it is actually not autolayout. As a next step, I define a cell class, where I put this xib view, something like this (sorry for objc code, almost done porting the project to swift):
- (void)awakeFromNib {
[super awakeFromNib];
[[NSBundle mainBundle] loadNibNamed:#"CellContent" owner:self options:nil];
// self.content is an outlet which gets the root of the XIB on load
[self.contentView addSubview:self.content];
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView.leftAnchor constraintEqualToAnchor:self.content.leftAnchor].active = YES;
[self.contentView.rightAnchor constraintEqualToAnchor:self.content.rightAnchor].active = YES;
[self.contentView.topAnchor constraintEqualToAnchor:self.content.topAnchor].active = YES;
[self.contentView.bottomAnchor constraintEqualToAnchor:self.content.bottomAnchor].active = YES;
}
As you might guess, when I run this the cell height is the big freeform one, like in the IB of the content XIB, so no auto layout. It seems like the freeform height is even added as another constraint and there is a constraint conflict which results in some of them being broken at runtime.
Let me know how can I fix this, easily create content views for my cells which have auto heights and which I can embed in my cells to benefit the tableView.rowHeight = UITableViewAutomaticDimension magic?
Maybe you forgot to set the Content Hugging Priority and Content Compression Resistence Priority of the Labels.enter image description here
I found what was my problem. Actually, there were conflicts with constraints added because of autoresizing. So I changed this line (from the question above):
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
to:
self.content.translatesAutoresizingMaskIntoConstraints = NO;
Stupid mistake, the translatesAutoresizingMaskIntoConstraints flag should be disabled for the child view after adding, not for the parent view, as I incorrectly did. After that the magic with cell auto height detection started working like charm.
Make sure UILabel has multiline property . So , Set 0 for Line Property at Attribute Inspector .
Make sure that UILabel Auto-Layout is properly relative to Left-Right-Top-Bottom .
Apply these delegate Functions :
-(CGFloat) tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return UITableViewAutomaticDimension;
}

iOS Dynamic Table View Cell Height Without UILabels

Is it possible to use this feature without using text related views? This would be to not have to calculate the height, but instead let the cell self-size using subviews with constant height constraints, and eventually changing those height constraints at runtime.
The proper way to handle dynamic cell height changes in a table view is through tableView:heightForRowAtIndexPath: in UITableViewDelegate. You can grab the height from the subviews but you have to link that value to the index path of the cell.
Yes,you could build a tableviewcell with dynamic height.
1.use constraints to configure your tableviewcell's subviews
2.add a line of code In viewdidload method:
self.tableView.rowHeight = UITableViewAutomaticDimension
3.in some cases you must add another line of code In viewdidload method:
self.tableView.estimatedRowHeight = 40 //or any value other than your tableviewcell's height in IB.
Yes you can do that.
First Add constraints to subviews and add the following code in .m file.
(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewAutomaticDimension;
}
-(CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewAutomaticDimension;
}

When to change cell constraints for automatic cell height for iOS 8?

My cell has an image which is part of the component for determining the height of it. It has a constant height constraint defined on the Storyboard and an outlet to it on the code.
I am trying to use iOS 8's automatic height.
On viewDidLoad I have this line: self.tableView.rowHeight = UITableViewAutomaticDimension.
A have also implemented the tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) method on the delegate.
The problem is I want to update the height constant when setting up the cell for display, but this doesn't seem to work because its height has already been calculated at this point. So I get this error on the log:
(
"<NSLayoutConstraint:0x7faed0854440 V:[winninapp.ScoreView:0x7faed08574d0(6)]>",
"<NSLayoutConstraint:0x7faed085b020 V:[UIImageView:0x7faed085ae10(211)]>",
"<NSLayoutConstraint:0x7faed0866700 V:|-(0)-[app.ScoreView:0x7faed08574d0] (Names: '|':UITableViewCellContentView:0x7faed085cf90 )>",
"<NSLayoutConstraint:0x7faed08667f0 V:[app.ScoreView:0x7faed08574d0]-(0)-[UIView:0x7faed085e180]>",
"<NSLayoutConstraint:0x7faed0866890 V:[UIImageView:0x7faed085ae10]-(0)-| (Names: '|':UITableViewCellContentView:0x7faed085cf90 )>",
"<NSLayoutConstraint:0x7faed08668e0 V:[UIView:0x7faed085e180]-(0)-[UIImageView:0x7faed085ae10]>",
"<NSLayoutConstraint:0x7faed0867d50 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7faed085cf90(44)]>"
)
What's the correct place to update the height constant for the cell?
I have tried calling these methods after updating the constant, but with no luck:
setNeedsUpdateConstraints()
setNeedsLayout()
layoutIfNeeded()
when i set up a simple tableviewcontroller with the following content (heightconstraint is the imageview's height constraint) everything works as expected:
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.estimatedRowHeight = 44.0;
self.tableView.rowHeight = UITableViewAutomaticDimension;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 10;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CustomCell" forIndexPath:indexPath];
cell.heightConstraint.constant = arc4random_uniform(50) + 20;
return cell;
}
here is what it looks like:
So, the way I finally got working without getting conflict warnings and the cell is displaying correctly was by removing the previous constraint and adding a new one.
I did this because I have changed the constraint from height to a kind of ratio and you cannot set the multiplier for a constraint and it ended up working.
I just added this to the constraint observer:
#IBOutlet weak var thumbnailRatio: NSLayoutConstraint! {
willSet {
videoThumbnail?.removeConstraint(thumbnailRatio)
videoThumbnail?.addConstraint(newValue)
}
}
By doing this way there is no need to call setNeedsLayout() or anything like that.
you can give this line
self.tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = expected max height
in viewDidLoad.
set number of rows to 0 and NSLinebreakMode to Word wrap or char wrap
[cell.textLabel setNumberOfLines:0];
[cell.textLabel setLineBreakMode:NSLineBreakByWordWrapping]
Instead of adjusting a height constraint in code, try constraining your image view to the top/bottom of the cell. The cell will size properly based on the size of the image view. You might need to change the fit of your image view to fill so that it shows the entire image.

how to get the frame of the label which is in the view in table view cell in ios

I have used the following code to dynamically change the row height in ios. But the height is too high. I dont know where i made mistake. My code is as follows:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(!self.customCell){
self.customCell = [self.goalDetailsTableview dequeueReusableCellWithIdentifier:#"GoalDetailsCell"];
}
NSMutableDictionary *cellData = [self.databaseCall transactionFromDatabase:indexPath.row goalId:self.goalId andStageId:self.stageId];
//Cell Layout
self.customCell.tipsDescription.text = [cellData objectForKey:#"tipsDescription"];
[self.customCell.tipsDescription sizeToFit];
//Height of cell
float height = (CGRectGetMaxY(self.customCell.tipsDescription.frame) + 20);
return height;
}
Note: tipsDescription is a UILabel inside the UIView which is kept inside the UITableViewCell.
You need to calculate your text height and based on it have to set cell height (yes, its going to be little mind expensive but at last you'll be happy!). Here's the one solution for this, Replacement for deprecated sizeWithFont: in iOS 7? and if you want some more info, Calculating number of lines of dynamic UILabel (iOS7) and this too UITableViewCell with UITextView height in iOS 7? (I know its not for UITextView but you can consider it with UILabel).
With iOS 8 we have self sizing table view cells. With it, there is no need to even implement heightForAtIndexPath.
Step 1) Set up constraints for your table cell
Step 2) In viewDidLoad:
self.tableView.estimatedRowHeight = 100.0; //or whatever value you need
self.tableView.rowHeight = UITableViewAutomaticDimension;
And that's it. I would also mention a few of us have ran into an issue where you might need to reload the table view's data in viewDidAppear.

How to remove UITableView's NSAutoresizingMaskLayoutLayoutConstraint?

I designed a custom UITableViewCell by adding some subviews in the cell's contentView, I also added some auto layout constraints between the contentView and the subviews.
But when I debug the app, Xcode tells me that there is a constraint conflict. In the list of constraint, there is one NSAutoresizingMaskLayoutLayoutConstraint that limits the cell height to be 43, so Xcode break the constraint of my subview height and 'compress' it.
I have tried:
In Interface builder, uncheck the "autoreize subviews" checkbox. Doesn't work.
In code, cell.contentView.translatesAutoResizingMaskIntoConstraints = NO. This causes the app to crash with an exception: "Auto Layout still required after executing -layoutSubviews". I have tried every proposed solution in this question: "Auto Layout still required after executing -layoutSubviews" with UITableViewCell subclass None of them work for me.
So I guess I can only let the cell do its autoresizing thing, and remove the auto resizing constraint in code. How should I do it without breaking things?
EDIT:
Or, from another perspective, how I can make the tableViewCell height flexible (changes with subview height and constraints)? In IB, I have to set its height, right?
You DON'T need to set cell.contentView.translatesAutoResizingMaskIntoConstraints = NO
In order to get the flexible height in UITableViewCells in autolayout you need to manually compute for the height in - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
Yes this tedious, but there is no other way. To calculate the height automatically you need to fulfill two conditions in your UITableViewCell:
You must make sure all your subviews have
translatesAutoResizingMaskIntoConstraints=NO
Your cell subview's constraints must be pushing against the top and bottom edges of the UITableViewCell.
Then in your - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath, You need to recreate that cell for the specific indexPath and compute the height manually for that cell.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath
*)indexPath
{
//Configure cell subviews and constraints
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
[self configureCell:cell forIndexPath:indexPath];
//Trigger a layout pass on the cell so that it will resolve all the constraints.
[cell setNeedsLayout];
[cell layoutIfNeeded];
//Compute the correct size of the cell and get the height. This is where the magic happens.
CGFloat height = [cartItemCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
height += 1.0f
return height;
}
Take note that systemLayoutSizeFittingSize is wonky with UITextViews. In that case you have to compute the height of the entire cell manually using another way. There are also some performance optimizations you can do by caching the height per indexPath.
This blog post has a a more detailed description on what to do but the gist is essentially what I mentioned above. : http://johnszumski.com/blog/auto-layout-for-table-view-cells-with-dynamic-heights
I have created some sample code for dynamic tableview cell height with auto layout. You can download the project using following link.
DynamicTableViewCellHeight
I hope this will help you.

Resources