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.
Related
I am designing an app in which i have used a table view.This table view uses a custom cell.I have given proportinal height to the table view.T
tableview height constraints:
equal height to mainview
multiplier:189:568
Cell properties
cell height:77
Image constraints:
Label constraints
TextViewContsraints
bottom right label constraints
Code to make the row height dynamic
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:
(NSIndexPath *)indexPath
{
float percentage = (77.0 / 568.0);
float height = self.view.frame.size.height * percentage;
return (height>77.0)?height:77.0;
}
Issue screen
Is the UITableViewCell in IB associated with a UITableViewCell subclass that overrides drawRect:? If so, make sure you are calling the super implementation, as that's what draws the line at the bottom. If you are overriding layoutSubviews make sure no views are obscuring the bottom of the cell.
In my case, i forgot to call [super layoutSubviews] after overriding the layoutSubviews method. Took me hoursto find the problem.
Hope this will help you.
Increase custom cell size to 80-88 may be it solve your problem
I change the frame of cells (add margins to the left and to the right) according to the code. The problem is cells update their frames only after they disappear and appear again via scrolling. I used table view's - reloadData as well, but it did't help. How do I force cells to be redrawn without scrolling?
- (UITableViewCell *) tableView: (UITableView *) tableView
cellForRowAtIndexPath: (NSIndexPath *) indexPath {
PersonTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: #"TableViewCellID"
forIndexPath: indexPath];
cell.frame = CGRectMake(20, cell.frame.origin.y, cell.frame.size.width-2*20, cell.frame.size.height);
/* tried any of those
[cell setNeedsDisplay];
[cell setNeedsLayout];
[cell layoutIfNeeded];
*/
return cell;
}
I'd recommend using .xibs for this. Much easier.
As far as I know, you are not able to change the frame of a cell in that manner. The cell's height is determined by heightForRowAtIndexPath, and the width is set to the width of the tableView.
You may be able to do it in some manner the way you are attempting, but the cleanest way I know is the following.
If you want there to be a margin around the cell, you can:
Create a nib for a UITableViewCell with a UIView containing all your views, and place a border using constraints.
Embed all your content inside a UIView (lets call this borderedContentView) and place this as the immediate subview of contentView
Place constraints relating borderedContentView to the contentView, with the leading, trailing, top and bottom constraints set to the values that create the border width you desire.
If your tableView has a backgroundColor, you'll have to set the contentView's backgroundColor to the same color as the tableView's backgroundColor so as to create the illusion of a margin. Do this in the tableView's delegate method willDisplayCell: or in a subclass of UITableViewCell awakeFromNib or other related method.
Bask in the glory of your margined cells.
You can also do this programmatically if you prefer not to use interface builder, but it is very easy to do in IB.
Hope this helps.
The solution to the problem is to override setFrame method of UITableViewCell. This way it perfectly works.
In my custom UITableViewCell I set the height of row as
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 70;
}
As a result (I believe), when data is rendered, the last cell is not rendered correctly.
This happens on all views where I set the heightForRowAtIndexPath. How do I fix it, so that I can see last cell as well?
You are returning the height of the cell correctly and this issue seems to be related with table view and not with the cell, I think you need to reduce the height of the tableView.
If you are using AutoLayout then you need to set constraints to your tableView with Respect to its superview.
Else if just set
[tableView setAutoresizingMask:UIViewAutoresizingMaskFelxibleHeight | UIViewAutoResizingMaskFlexibleWidth];
Perhaps your UITableView's height is not right, check it.
I have a UITableViewCell that has two views in it that are dynamically sized. How would I handle something like this in terms of the constraints used, where to actually set the dynamic heights (height for row? cell for index?). I've been cracking away at this for a few hours but can't seem to figure it out, thanks.
If the dynamic heights are just based on content, constraints like this "V:|[fx1][dh1][dh2][fx2]|" will get you unpredictable results. Autolayout will try it's best, but one of the dynamic views will "giveup" it's intrinsic size to fill up the space.
I think you have two options:
Give the first "dynamic" view a higher content hugging priority
This would perhaps work if you need all cells to be the same height in your table.
[self.dh1 setContentHuggingPriority:UILayoutPriorityRequired
forAxis:UILayoutConstraintAxisVertical];
[self.dh2 setContentHuggingPriority:UILayoutPriorityDefaultLow
forAxis:UILayoutConstraintAxisVertical];
Size the whole cell according to it's content
Don't think of it as having to fit stuff into the cell. Size the cell to fit it's content instead.
Use constraints like this: "V:|[fx1(20)][dh1][dh2][fx2(20)]" ... without 'anchoring' the last fixed view to the bottom
Then, in heightForRowAtIndexPath, layout out the cell in the background and measure it.
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:#"CELL"];
// etc...
[cell setNeedsLayout];
[cell layoutIfNeeded];
return cell.fx2.frame.origin.y + cell.fx2.frame.size.height;
}
I'm using Apple's DateCell sample Xcode project to figure out how to use a UIPickerView inside of a UITableViewCell, but I'm having some trouble figuring out the constraints that the sample code has set up for the UIDatePicker in the storyboard.
Link: https://developer.apple.com/library/ios/samplecode/DateCell/Introduction/Intro.html
It says that the UIDatePicker has a constraint relative to the actual UITableViewCell, but when I try to set up a constraint between the two, I can't. Ctrl-dragging from the picker to the cell doesn't highlight the cell. I tried doing it with the cell's content view rather than the cell itself, but that doesn't quite produce the same result as in the sample code's storyboard.
These are the constraints set up by the project for the date picker:
And for the cell:
What the sample's storyboard looks like:
How can I reproduce the above image using constraints?
Any help would be greatly appreciated.
I found that the sample's static height was not what I wanted to use anyway, since I have some cells with lots of text that need to have variable heights for each cell. So instead of the approach in the sample which grabs the height of the picker cell from the pre-defined storyboard size, I used UITableViewAutomaticDimension to automatically fit all of my cells for height. If you don't want auto height, it's still pretty easy to adapt the following solution to set the height to a static value, as I mention in step 3.
Set tableview row height to auto when the view loads:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.tableView.estimatedRowHeight = 60.0; // Set your average height here.
self.tableView.rowHeight = UITableViewAutomaticDimension;
}
Add an estimated height so that it works properly (at least in iOS 8). If you don't add this, you might get some buggy behavior when it draws the cell heights.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
For auto height, remember to remove the tableView:heightForRowAtIndexPath: definition if you have one, as it could interfere with the auto settings.
// Don't define this at all if you want the picker cell to have auto height.
//- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
//{ }
If you want to use a static height for the picker cell, then define the tableView:heightForRowAtIndexPath: function and just return the static value here instead of using the auto dimension like the sample does it. Example would be something like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// This would return the static height for the picker, but auto heights for the rest of the cells.
return ([self indexPathHasPicker:indexPath] ? self.pickerCellRowHeight : UITableViewCellAutoDimension);
}
And then you'd modify the sample to set your static height instead of grabbing it from the frame value in viewDidLoad.
// obtain the picker view cell's height, works because the cell was pre-defined in our storyboard
//UITableViewCell *pickerViewCellToCheck = [self.tableView dequeueReusableCellWithIdentifier:kDatePickerID];
self.pickerCellRowHeight = 216;//CGRectGetHeight(pickerViewCellToCheck.frame);
Rather than trying to recreate the same constraints from the sample, I told storyboard to Reset to Suggested Constraints for the UIPickerView, and then added one more. It added the first two in the picture, and then I just added one more for aligning the center X. My view happened to have the shown numbers from the auto settings but yours may be different.
In case you haven't already done it, part of making the picker work is to make sure the dataSource and delegate for the UIPickerView are set to the view controller using storyboard and control clicking and dragging.