I have a button in my UITableViewCell which has to expand that cell. I have some vertical layout constraints in my cell which I have to modify upon expanding to rearrange the subviews.
I returned the height of each cell in tableView:heightForRowAtIndexPath:
After the user scrolls, the expanded cell has to be still expanded when it comes back on the screen, of course. So in tableView:cellForRowAtIndexPath: (I tried it also in tableView:willDisplayCell:forRowAtIndexPath: with the same result) I do something like:
if ([_indexPathOfExpandedCell isEqual:indexPath]) {
[cell setConstraintToLargeValue];
} else {
[cell setConstraintToSmallValue];
}
The problem is, when the expanded cell comes back to the screen and the setConstraintToLargeValue gets called, when I try to increase the constant property of the vertical constraint, I get an
Unable to simultaneously satisfy constraints
message. What I figured out so far is that at the point of modifying the constraint the cell has the correct height (the one I return in heightFor...), however the cell.contentView has a much smaller height from before the expansion and this is what is causing the the error. Does anybody have a solution to this? Thanks in advance.
Found the solution. Instead of triggering the change of the constraint from tableView:cellForRowAtIndexPath:, I only set a flag on the cell isExpanded, and modify the constraint in the layoutSubviews method, where after calling [super layoutSubviews] the cell.contentView becomes the same height as cell and I no longer get conflict between the constraints.
Related
I have an UITableViewCell with UILabel which correctly resizes its height according to text length. To accomplish this I have correctly set AutoLayout constraints and specified
tableView.rowHeight = UITableViewAutomaticDimension
The problem I am having is when the test of the UILabel gets longer after pressing a UIButton - READ MORE, in the cell itself. I programmatically add text to the UILabel and I expect the cell height to grow. I have added:
self.layoutIfNeeded()
self.updateConstraints()
in the cell, but it is not working. Cell stays same height.
I have also forced a reload of the cell row in the delegate UIViewController
but it is not working either. Am I missing something basic here?
EDIT: The question differ from other similar questions, because I'd like the answer to avoid having to calculate the height manually. Since AutoLayout is perfectly able to calculate correct dynamic height at first load, I guess there should be a way to make it redraw the cell once the text in the label gets longer, without the need of performing manual calculation to estimate row height.
Get the Label height dynamically and set the tableviewcell height
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Calculate the Label height and add it to default height of the cell
}
I have found a solution that works, after updating the UILabel content I am calling:
UIView.animateWithDuration(0.3) {
cell.contentView.layoutIfNeeded()
}
To update cell constraints. And then to resize the cell height:
tableView.beginUpdates()
tableView.endUpdates()
I will wait to accept this answer to see if someone else will answer a more elegant solution though.
I'm using iOS 9.2 and XCode 7.2.
I have a basic UITableView object in which i add different kind of UITableViewCell subclasses.
With one of them, when i set manually the height overriding the method heightForRowAtIndexPath, i don't get any content on the cell.
If i return -1 as height(the default value for the UITable row height), i get my cell showing up correctly. The thing is that i do need a different height for this row because the content is quite big.
here is the code for heightForRowAtIndexPath:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
MenuItem *menuItem = [menuManager menuItemAtIndex:indexPath.row];
if ([menuItem type] == MenuItemTypeEditorHeader) {
return 100;
} else {
return [super tableView:tableView heightForRowAtIndexPath:indexPath];
}
}
MenuItem is a class containing the specific menu object' informations, such as the type. The result is that the cell is showed up at the correct height, but it's empty.
Its not advisable to use heightForRowAtIndexPath anymore - thats old-school. Instead, do this :
Set up autolayout constraints in your cell (if you dont know how to - you need to, its not something you can avoid anymore!)
Create an estimatedRowHeight for autolayout to use, on the tableView. You can set it in the nib/storyboard or programmatically, in viewDidLoad for eg, like this :
self.tableview.estimatedRowHeight = 68.0;
Set your tableview to use 'automatic dimension', like this :
self.tableview.rowHeight = UITableViewAutomaticDimension;
And thats it. If you do these things, then your cells will vary in height according to their constraints. So if one of your subclasses has a height of 150px due to its constraints, that will work perfectly next to another subclass that has a height of 50px. You can also vary the height of a cell dynamically depending on the contents of the cell, for eg when you have labels that expand using 'greater than or equal to' constraints. Also - simply omit the 'heightForRowAtIndexPath' method, you dont need to implement it at all.
Are you calling tableView.reloadData() ?
print the length of menu objects before you call tableView.reloadData().
HeightForRowAtIndexPath just returns height of a row. So may be problem in cellForRowAtIndexPath.
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.
There's more content in my Table View, but this cell is getting cut off and not showing more content:
... So I'm not able to scroll any more, even there is more content.
If I pull up with my finger it shows more content, but then when I let my finger off the cell it goes back to the state show in the image above.
I've tried making sure I set height and width in the Labels and Images in AutoLayout since I thought that might be a problem, but still hasn't fixed it.
Any ideas? Thanks!
UPDATE - Table View structure in Storyboard
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
id model = self.model[indexPath.row];
if ([model isKindOfClass:[DBase self]]) {
return 520;
}
else {
return tableView.rowHeight; // return the default height
}
}
I was having this problem as well but mine was only cutting off a single cell. I solved it and thought I'd post this here as it may help others in the future
I was using custom cells created from nibs. Some of my cells could change heights so I was also using this:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 61
I figured out the issue was that I was setting top, left, right and height constraints on my view. I needed to set the bottom constraint as well or else my row height would be way smaller than it should be.
So if you are having this problem check if your row heights aren't smaller than the views that they contain.
I suggest adding height and width constraints to your UITableViewContoller or UITableView in the storyboard.main.
Depending on the size of your device, the UITableView size will remain constant unless you set constraints that will work across all devices.
I have a grouped tableView in my iPad-app, and I've been trying to set cell.imageView.center = cell.center to center the image instead of putting it to the leftmost position. This is apparently not possible without a subclass of the UITableviewCell(If someone could explain why, that'd also be appreciated.. For now I just assume they are 'private' variables as a Java-developer would call them).
So, I created a custom tableViewCell, but I only want to use this cell in ONE of the rows in this tableView. So in cellForRowAtIndexPath I basically write
cell = [[UITableViewCell alloc]initWith//blahblah
if(indexPath.row == 0)
cell = [[CustomCell alloc]initWith//blahblah
This is of course not exactly what I'm writing, but that's the idea of it.
Now, when I do this, it works, but the first cell in this GROUPED tableView turns out wider than the rest of them without me doing anything in the custom cell. The customCell class hasn't been altered yet. It still has rounded corners though, so it seems it knows it's a grouped tableView.
Also, I've been struggling with programmatically getting the size of a cell, in cellForRowAtIndexPath, I've tried logging out cell.frame.size.width and cell.contentView.frame.size.width, both of them returning 320, when I know they are a lot wider.. Like, all the rows are about 400 wide, and the first cell is 420 or something. It still writes out 320 for all the cells..
This code will not work for a couple of reasons:
cell.imageView.center = cell.center;
Firstly, the center is relative to its superview. I believe the cells superview is the tableView. The imageView's superview will be the content view of the cell. Therefore the coordinate systems are different so the centens will be offset. E.g. the 3rd cell down will have a center of 0.5 widths + 3.5 heights. You should be able to ge around this issue by doing:
cell.imageView.center = CGPointMake( width / 2 , height / 2 );
The second issue is related to how the table view works. The table view manages its cells view's. The width of a cell is defined by the table view's width and the height is defined by the table view's row height property. This means the cell itself has no control over its size.
You can however size its subviews, but you must do this after the cells size has been set (otherwise you can get strange results). You can do this in layout subviews (of the custom UITableViewCell class). See this answer.
- (void)layoutSubviews {
[super layoutSubviews];
self.imageView.frame = ....
}
When layoutSubviews is called the cells frame has been set, so do your view logging here instead of cellForRowAtIndexpath.
As for the GROUPED style. Im not sure if this is designed to work with custom views. I suspect it sets the size of its cells to its own width minus a 20 pixel margin on each size, then applies a mask to the top and bottom cells in a section to get the rounded effect. If you are using custom view try to stick with a standard table view style.