I have a TableView whose first element is a fixed aspect ratio header image. I embedded a UIImageView inside the UITableViewCell's Content View. The desired outcome is the entire cell adopting the size of the image. I tried a couple of things, first of all, pinning all four edges of the UIImageView to the parent Content View and setting the aspect ratio to my desired value (2h by 3w). With this setup the aspect ratio constraint gets completely ignored and the image view takes up the height of the cell, which shrinks my asset. Next, I tried to remove the bottom constraint. As a result, the aspect ratio is respected, but the cell height is larger than that of the image view.
My question is, can I make the cell shrink to wrap the height of the image view using auto layout?
Can I make the cell shrink to wrap the height of the image view using autolayout?
Yes you can, but there is a trick.
UITableViews have a fixed height for all their cells. If you want variable height you must implement UITableViewDelegate method
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
If your cell constraints are properly setup (and by that I mean if instead of a cell you had a UIView, it's size would be defined by it's content), you will be able to do something like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
MyCell *cell = [tableView dequeueReusableCellWithIdentifier:kIdentifier];
// setup your cell image
return [cell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
}
The problem is, if you are retrieving the images from the web, there is no possible way that you know the size of the image without loading it.
If you are using local images, this should work.
Related
I've got constraints for a table view cell as follows (the majority are XCode's suggested constraints).
But when the code is executed the row height of the cell is about half of what it is in the canvas.
Initially I didn't have an aspect ratio constraint and the bottom half of the image was missing when the view controller runs, after adding an aspect ratio constraint instead the image is squashed.
I don't understand why this is happening. Why when the height of the image view constraint has priority 1000 is it being ignored?
Try to set the height of the cell in
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50;
}
like that it's look like you are not set the heightForRowAtIndexPath so that it tooks the default hight of 44
I'm attempting to dynamically resize a custom UITableViewCell to fit an attributed string's content. This is the result:
When I initialize my tableView I do the following:
self.chatsTableView.estimatedRowHeight = 72.0;
self.chatsTableView.rowHeight = UITableViewAutomaticDimension;
I also implemented the following method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
As you can see it is resizing my height (the smaller allowed is 72). However, for some reason it's creating a weird effect. I'm using storyboard, and auto layout.
Move it to the - (void)tableView:(UITableView *)tableView
willDisplayCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath method.
You have to set estimatedRowHeight to UITableViewAutomaticDimension as well.
It should work only with the two properties you set , the row height and the estimated row height.
first try to remove the delegate method.
If the table "Jumps" a little when you scroll (because of a bug) then also implement the two delegate methods, heightForRow and estimatedHeightForRow and return the same values as you set in the properties , estimatedRowHeight = 72 and rowHeight = UITableViewAutomaticDimension.
If it's still doesn't work then I would double check the constraints.
For iOS 8+
Set up cell constraints. Make sure that ALL subviews that affects cell height are vertical connected. In this case: top edge of top label is pinned to superview's top AND top labels bottom edge is pinned to bottom's label top edge AND bottom's label bottom edge is pinned to superview's bottom. Vertical constraints chain have to be closed. In your case it looks like a bottom edge of bottom label is not pinned to contentViews bottom edge.
When dequeuing cell for reuse always use
dequeueReusableCellWithIdentifier:forIndexPath: not a shorter version without indexpath specified.
Set self.tableView.rowHeight = UITableViewAutomaticDimension;
Set self.tableView.estimatedRowHeight = 50.0;. This step is optional but helps table view proper manage things like scroll indicators etc.
Will work :)
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.
I want my UITableView to always show 4 rows, whether it's on retina 3.5 or 4 - with appropriate re-sizing of fonts and subviews in the cell. The app will always be in portrait orientation.
Is there an "elegant" way to do this in storyboard or through constraints?
Or is the best way to determine all the sizes manually, then alter them programmatically?
Set Auto-resize property of UITableView with flexible height, so that it could change height on devices having different screen sizes. Use heightForRowAtIndexPath like this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return self.yourTableView.frame.size.height/4;
}
Now, whatever the height of tableview is, you will get same cell heights for all 4 cells.
Note: For your UITableViewCells set view and elements resize properties like flexible height and flexible positions, so that cells' elements get adjusted according to height changes.
I dont understand your question, but if you want to specify the cell height, you can use a UITableViewDelegate method:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
I have a UITableViewCell that is implemented using storyboard that looks like:
Here is what the cell should look like without an image:
I have been fiddling with the constraints and banging my head trying to figure this out but have had no luck. I have a pretty good understanding of constraints and how to add them programmatically but have had no luck with this specific problem and feel like I am just adding layout constraints to the cell willy-nilly with no logical thought process. The cell represents a newsfeed post which may or may not have an image in the main image view at the top, and should behave as follows. If the cell doesn't have an image in it the bottom bar with the like and comment counts, moves up to align with the top of the cell. I achieved this behaviour by setting a constraint that kept the smaller image view, post title, post time and the post content a set distance away from the bottom of the cell. This approach works and when the cell is resized in the heightForRowAtIndexPath method the subviews move appropriately. The problem comes when the text in the post content is larger then a single line. The height of the cell adjusts correctly but the top of the text view stays at the same location and grows downward and overflows into the next cell. When I place the constraints to align the four subviews with the top of the cell I run into issues when there is no image and the post content is larger then a single line. In this case, the cell resizes to be smaller than its original size and the subviews stay at the distance specified by the constraint. The smaller image, post title, time and content are clipped and don't display. This is such an odd problem with so many different cases. I have been working at this for almost two days and could really use someone else's thoughts on how to solve this issue. I hope this isn't too confusing, thanks for the help!
I have one way to solve this, but I'm sure there are many others. I gave both image views a fixed height constraint. The small image view and the top label (Post Title) have fixed heights to the top of the cell -- both of these as well as the height constraint of the large image view have IBOutlets to them so they can be changed in code. The bottom label (Post Content) has its number of lines set to 0, and has an IBOutlet to its height constraint (all the labels had the standard 21 point height to start). In code, I check for the existence of an image at each indexPath, and change the constraints accordingly.
- (void)viewDidLoad {
UIImage *image1 = [UIImage imageNamed:#"House.tiff"];
[super viewDidLoad];
self.theData = #[#{#"pic":image1, #"post":#"short post"},#{#"post":#"short post"},#{#"pic":image1, #"post":#"Long long post with some extra stuff, and even some more"},#{#"post":#"Long long post with some extra stuff, and even some more"}];
[self.tableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.theData.count;
}
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat ivHeight = (self.theData[indexPath.row][#"pic"])? 215 : 0; // 215 is the fixed height of the large image view
CGSize labelSize = [self.theData[indexPath.row][#"post"] sizeWithFont:[UIFont systemFontOfSize:17] constrainedToSize:CGSizeMake(152, CGFLOAT_MAX)];
return 140 + ivHeight + labelSize.height; // the 140 was determined empirically to get the right spacing between the 3 labels and the bottom bar
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
RDCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
cell.label.text = self.theData[indexPath.row][#"post"];
cell.iv.image = self.theData[indexPath.row][#"pic"];
if(self.theData[indexPath.row][#"pic"] == nil){
cell.heightCon.constant = 0; // heightCon is the outlet to the large image view's height constraint
cell.ivTopCon.constant = 8; // ivTopCon is the outlet to the small image view's spacing to the top of the cell
cell.labelTopCon.constant = 8; // labelTopCon is the outlet to thetop label's spacing to the top of the cell
}else{
cell.heightCon.constant = 215; // this number and the following 2 are taken from the values in IB
cell.ivTopCon.constant = 185;
cell.labelTopCon.constant = 233;
}
CGSize labelSize = [self.theData[indexPath.row][#"post"] sizeWithFont:[UIFont systemFontOfSize:17] constrainedToSize:CGSizeMake(152, CGFLOAT_MAX)];
cell.labelHeightCon.constant = labelSize.height;
return cell;
}
Hey #rdelmar thanks for the solution! Eventually I ended up just designing two different cells in the storyboard file with different reuse identifiers but the same subclass. I then checked in the cellForRowAtIndexPath method if the cell had content or not, and assigned the correct identifier. If this is the incorrect way of doing this, or will cause problems down the road please let me no in the comments.