I have a uilabel setup in a view. It doesn't have a width constraint, but its width is instead determined by a leading constraint to the thumbnail image, and a trailing constraint to the edge of the view.
The label is set to have 0 lines, and to word wrap. My understanding is that this should cause the frame of the uilabel to grow, and indeed it does sometimes. (Previous to auto layout, I would calculate and update the frame of the label in code).
So the result is, it works correctly in some instance and not others. See most cells working correctly there, but the last cell appears to be too big. In fact it's the right size. The title "Fair Oaks Smog Check Test" actually ends with "Only". So my calcuation for the cell size is right, it should be that size. However the label doesn't wrap the text for whatever reason. It's frame width does not extend off to the right, so that's not the issue.
So what is going on here? It's 100% consistent, always on that cell and not the ones above it, which makes me think it's related to the size of the text, and UILabel isn't re-laying out the text once this view is added to the cell (which makes it actually smaller width wise).
Any thoughts?
Some additional information
The height of the cells is calculated from one sample cell I create and store in a static variable:
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.items.count == 0) {
return 60;
}
static TCAnswerBuilderCell *cell = nil;
static dispatch_once_t pred;
dispatch_once(&pred,
^{
// get a sample cellonce
cell = [tableView dequeueReusableCellWithIdentifier:TC_ANSWER_BUILDER_CELL];
});
[cell configureCellWithThirdPartyObject:self.items[indexPath.row]];
return [cell heightForCellWithTableWidth:self.tableView.frame.size.width];
}
I configure the cell with my data object on the fly, and then call a method I have on it which calculates the height of the cell with a given table width (can't always rely on the cell frame being correct initially).
This in turn calls a height method on my view, since it is really where the label lives:
- (CGFloat)heightForCellWithTableWidth:(CGFloat)tableWidth {
// subtract 38 from the constraint above
return [self.thirdPartyAnswerView heightForViewWithViewWidth:tableWidth - 38];
}
This method determines the height by figuring out the correct width of the label, and then doing a calculation:
- (CGFloat)heightForViewWithViewWidth:(CGFloat)viewWidth {
CGFloat widthForCalc = viewWidth - self.imageFrameLeadingSpaceConstraint.constant - self.thumbnailFrameWidthConstraint.constant - self.titleLabelLeadingSpaceConstraint.constant;
CGSize size = [self.titleLabel.text sizeWithFont:self.titleLabel.font constrainedToSize:CGSizeMake(widthForCalc, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
CGFloat returnHeight = self.frame.size.height - self.titleLabel.frame.size.height + size.height;
CGFloat height = returnHeight < self.frame.size.height ? self.frame.size.height : returnHeight;
return height;
}
This works 100% correctly.
The cells are created obviously in cellForRowAtIndexPath and immediately configured:
if (self.items.count > 0) {
TCAnswerBuilderCell *cell = [tableView dequeueReusableCellWithIdentifier:TC_ANSWER_BUILDER_CELL forIndexPath:indexPath];
[cell configureCellWithThirdPartyObject:self.items[indexPath.row]];
return cell;
}
In configuration of the cell, my view is loaded from a nib (it's re-used elsewhere, which is why it's not directly in the cell). The cell adds it as follows:
- (void) configureCellWithThirdPartyObject:(TCThirdPartyObject *)object {
self.detailDisclosureImageView.hidden = NO;
if (!self.thirdPartyAnswerView) {
self.thirdPartyAnswerView = [TCThirdPartyAPIHelper thirdPartyAnswerViewForThirdPartyAPIServiceType:object.thirdPartyAPIType];
self.thirdPartyAnswerView.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:self.thirdPartyAnswerView];
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"|[_thirdPartyAnswerView]-38-|" options:NSLayoutFormatAlignAllCenterY metrics:nil views:NSDictionaryOfVariableBindings(_thirdPartyAnswerView)]];
}
[self.thirdPartyAnswerView configureViewForThirdPartyObject:object forViewStyle:TCThirdPartyAnswerViewStyleSearchCell];
}
Finally my view configuration looks like this:
- (void) configureViewForThirdPartyObject:(TCTPOPlace *)object forViewStyle:(TCThirdPartyAnswerViewStyle) style {
self.titleLabel.text = object.name;
self.addressLabel.text = [NSString stringWithFormat:#"%#, %#, %#", object.address, object.city, object.state];
self.ratingsLabel.text = [NSString stringWithFormat:#"%d Ratings", object.reviewCount];
NSString *ratingImageName = [NSString stringWithFormat:#"yelp_star_rating_%.1f.png", object.rating];
UIImage *ratingsImage = [UIImage imageNamed:ratingImageName];
if (ratingsImage) {
self.ratingImageView.image = ratingsImage;
}
if (object.imageUrl) {
[self.thumbnailImageView setImageWithURL:[NSURL URLWithString:object.imageUrl] completed:nil];
}
}
A sort of solution, but I don't understand why
My subview was designed at 320 width, but has no constraints of its own for width
The subview was added to the cell, but with horizontal constraints that look like this:
#"|[_thirdPartyAnswerView]-38-|"
The view was configured immediately after being added to the cell, meaning the text for the titleLabel was set right then.
For whatever reason, the text was laid out as if the view had the full 320 instead of 282.
The label was never updated, even though the frame of the subview was updated to 282, and there were constraints on the label that would keep it sized correctly.
Changing the size of the view in the xib to be 282 fixed the issue, because the label has the right size to begin with.
I'm still not understanding why the label doesn't re-lay out after the size of the parent view is updated when it has both leading and trailing constraints.
SOLVED
See Matt's answer below: https://stackoverflow.com/a/15514707/287403
In case you don't read the comment, the primary problem was that I was unknowingly setting preferredMaxLayoutWidth via IB when designing a view at a bigger width than it would be shown (in some cases). preferredMaxLayoutWidth is what is used to determine where the text wraps. So even though my view and titleLabel correctly resized, the preferredMaxLayoutWidth was still at the old value, and causing wrapping at unexpected points. Setting the titleLabel instead to it's automatic size (⌘= in IB), and updating the preferredMaxLayoutWidth dynamically in layoutSubviews before calling super was the key. Thanks Matt!
I'm someone who has written an app that uses autolayout of five labels in a cell in a table whose cells have different heights, where the labels resize themselves according to what's in them, and it does work. I'm going to suggest, therefore, that the reason you're having trouble might be that your constraints are under-determining the layout - that is, that you've got ambiguous layout for the elements of the cell. I can't test that hypothesis because I can't see your constraints. But you can easily check (I think) by using po [[UIWindow keyWindow] _autolayoutTrace] when paused in the debugger.
Also I have one other suggestion (sorry to just throw stuff at you): make sure you're setting the label's preferredMaxLayoutWidth. This is crucial because it's the width at which the label will stop growing horizontally and start growing vertically.
I had the same problem and solved it using a suggestion from this answer. In a subclass of UILabel I placed this code:
- (void)layoutSubviews
{
[super layoutSubviews];
self.preferredMaxLayoutWidth = self.bounds.size.width;
}
I don't understand why this is not the default behavior of UILabel, or at least why you cannot just enable this behavior via a flag.
I am a little concerned that preferredMaxLayoutWidth is being set in the middle of the layout process, but I see no easy way around that.
Also, check that you are passing integral numbers to your layout constraints in code.
For me it happened that after some calculations (e.g. convertPoint:toView:), I was passing in something like 23.99999997, and eventually this lead to a 2-line label displaying as a one-liner (although its frame seemed to be calculated correctly). In my case CGRectIntegral did the trick!
Rounding errors could kill ya :)
Related
I'm trying to adjust a UITableViewCell's size to its content. This is basically for a chat view, where I list all previous messages and allow the user to scroll the conversation content.
So I have a UITableView with a few different cell prototypes. Incoming text messages, outgoing text messages, incoming image messages, outgoing image messages, and so on. Inside each cell's content view I have a standard UIView which I intent to use to draw the chat balloon. This view takes almost the cell's inner space (8px offset to the top, left, bottom, and right, all around). Inside that view I want the content. In the case of the text cells (incoming and outgoing) I want a UITextView which will display a text message. This is what I mean:
In yellow is the UIView and inside it the UITextView. Now I want to adjust everything to the text's size. I managed to accomplish the following:
sizeToFit accomplishes exactly what I need for the UITextView
I'm still not sure how to adjust the UIView's size to the UITextView's size.
To adjust the cell's height maybe I could use heightForRowAtIndexPath. I don't need (nor do I think I should) to adjust the cell's width. But a few regards on that: when is this method called? Will the cell already have been instantiated? Will it have already layed out the subviews? Otherwise, how can I tell the content's size?
Any input on this is appreciated!
Edit:
I managed to make a few progresses by following the tutorial posted by #vikingosegundo, but I'm stuck again. This is what I have:
So, basically: the text view has constraints for leading, trailing, distance to top, and distance to bottom. The containing view, on the other hand, has constraints for trailing and distance to top, so that if the size is small then it snaps to the right. I can't had leading constraints or otherwise it will always take the full width of the cell. I'm not sure about distance to bottom constraints.
When a enter a small message it looks great. It's well sized and it snaps to the right.
However, long messages don't span to several lines. Instead it still snaps to the right (OK), but the width grows to the left, indefinitely.
The cell is already adjusting its height to the content's height:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
HyConversationTableViewCell * cell = (HyConversationTableViewCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];
CGSize size;
[cell setNeedsLayout];
[cell layoutIfNeeded];
size = [cell.textMessageView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size.height + 32;
}
I'm guessing that what I need now is something like a text view's maximum width or something, but I realise that's not possible. How do I solve this?
Edit: If I had a leading constraint to the containing view it looks great when the text spans multiple lines, but not when it doesn't. Here's what it looks like:
And:
Edit: As suggested by Alex Zavatone, I changed tableView:heightForRowAtIndexPath: to the following:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
HyConversationTableViewCell * cell = (HyConversationTableViewCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];
CGSize overflowSize = CGSizeMake(cell.textMessageView.frame.size.width, FLT_MAX);
CGSize sizeAdjusted = [cell.textMessageView sizeThatFits:overflowSize];
return sizeAdjusted.height + 32.0f;
}
It shows a little better as the height is already adjusted, but the behaviour is somewhat erratic. Here's what it looks like at the beginning:
So the height is correct, but the text view does not adjust its width. Also, if I scroll the cells out of screen and then back in (which forces them to redraw) they start behaving erratically in what seems a random criteria. Here's a sample:
Sometimes this happens to the last two cells...
Edit: That last part was fixed by setting Content Hugging Priority and Content Compression Resistance Priority to required and the Intrinsic Size to Placeholder. Now the height shows properly.
If you are using iOS 8 you can use UITableViewAutomaticDimension.
You can check out this example
self.tableView.rowHeight = UITableViewAutomaticDimension;
You can take a look also on this video : What's New in Table and Collection Views in the 2014 WWDC.
Here how, we are doing that
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
AFMediaWithHeadlineCell *cell = [[[NSBundle mainBundle] loadNibNamed:#"AFMediaWithHeadlineCell"
owner:nil
options:nil] firstObject];
[cell loadText:#"Some text"];
return [cell height];
}
actually loadText: loads data into UI, and sizeToFits it.
and height is basic method that calculates cell's height
- (void)loadText:(NSString *)aText
{
self.textView.text = aText;
[self.textView sizeToFit];
}
- (CGFloat)height
{
return self.textView.frame.origin.y + self.textView.frame.size.height + 10; // 10 is margin
}
You can resize the text by setting the height to a large number and then using sizeThatFits on it.
Not sizeToFit.
Like so:
UILabel *label = self.prototypeCell.descriptiveText;
label.numberOfLines = 0;
CGSize sillyLargeHeight = CGSizeMake(label.frame.size.width, 9999);
CGSize labelFrameAdjustedForHeight = [label sizeThatFits:sillyLargeHeight];
return labelFrameAdjustedForHeight.height + 24.0; // 24 is 12 above and 12 below padding.
You can use a label or a textView. If you choose to use a UILabel, you'll need to set the # of lines to 0 so that it will be multiple line.
You can do this within - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
You'll also need to do the same adjustments to set the height on the field (use an IBOutlet) in the willDisplayCell method.
I have a UITableViewCell subclass which has an image, title and description.
I am supposed to resize the cell height according to the description content length i.e. if it spans more than 5 lines, I should extend it (+other subviews like image etc) till it lasts.
The next coming cells should begin only after that.
I have my UITableViewCell subclass instantiated from xib which has a fixed row height = 160.
I know this is pretty standard requirement but I am unable to find any guidelines.
I already extended layoutSubViews like this:
- (void) layoutSubviews
{
[self resizeCellImage];
}
- (void) resizeCellImage
{
CGRect descriptionRect = self.cellDescriptionLabel.frame;
CGRect imageRect = self.cellImageView.frame;
float descriptionBottomEdgeY = descriptionRect.origin.y + descriptionRect.size.height;
float imageBottomEdgeY = imageRect.origin.y + imageRect.size.height;
if (imageBottomEdgeY >= descriptionBottomEdgeY)
return;
//push the bottom of image to the bottom of description
imageBottomEdgeY = descriptionBottomEdgeY;
float newImageHeight = imageBottomEdgeY - imageRect.origin.y;
imageRect.size.height = newImageHeight;
self.cellImageView.frame = imageRect;
CGRect cellFrame = self.frame;
cellFrame.size.height = imageRect.size.height + imageRect.origin.y + 5;
CGRect contentFrame = self.contentView.frame;
contentFrame.size.height = cellFrame.size.height - 1;
self.contentView.frame = contentFrame;
self.frame = cellFrame;
}
It pretty much tells that if description is taller than image, we must resize the image as well as cell height to fit the description.
However when I invoke this code by doing this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.cellDescriptionLabel.text = #"Some long string";
[cell.cellDescriptionLabel sizeToFit];
[cell setNeedsLayout];
return cell;
}
It appears that while cell frame is changed due to layoutSubViews call, other cells do not respect it. That is, they appear on the same position had the previous cell would not have resized itself.
Two questions:
How to make it possible what I want?
Am I doing right by calling setNeedsLayout within cellForRowAtIndexPath?
P.S.: I know heightForRowAtIndexPath holds key to changing the cell height, but I feel that the data parsing (not shown here) that I do as part of cellForRowAtIndexPath would be an overkill just to calculate height. I need something that can directly tell the UITableViewCell to resize itself according to content needs.
-tableView:heightForRowAtIndexPath: is by design how variable sized cells are calculated. The actual frame of a cell is of no importance and is changed by the table view to fit its needs.
You are sort of thinking of this backwards. The delegate tells the table view how cells need to be drawn, then the table view forces cells to fit those characteristics. The only thing you need to provide to the cell is the data it needs to hold.
This is because a table view calculates all the heights of all the cells before it has any cells to draw. This is done to allow a table view to size it's scroll view correctly. It allows for properly sized scroll bars and smooth quick-pans through the table view. Cells are only requested when a table view thinks a cell needs to be displayed to the screen.
UPDATE: How Do I Get Cell Heights
I've had to do this a couple of times. I have my view controller keep a cell which is never used in the table view.
#property (nonatomic) MyTableViewCell *standInCell;
I then use this cell as a stand in when I need measurements. I determine the base height of the cell without the variable sized views.
#property (nonatomic) CGFloat standInCellBaseHeight;
Then in -tableView:heightForRowAtIndexPath:, I get the height for all my variable sized views with the actual data for that index path. I add the variable sized heights to my stand in cell base height. I return that new calculated height.
Note, this is all non-autolayout. I'm sure the approach would be similar, but not identical to this, but I have no experience.
-tableView:heightForRowAtIndexPath: is the preferred way to tell tableview the size of its cells. You may either precalculate and cache it in a dictionary and reuse, or alternatively in ios7, you can use -tableView:estimatedHeightForRowAtIndexPath: to estimate the sizes.
Take a look at this thread - https://stackoverflow.com/questions/18746929/using-auto-layout-in-uitableview-for-dynamic-cell-layouts-variable-row-heights, the answer points to a very good example project here - https://github.com/caoimghgin/TableViewCellWithAutoLayout.
Sorry, but as far as I know you have to implement tableView:heightForRowAtIndexPath:. Warning, in iOS 6 this gets called on every row in you UITableView right away, I think to draw the scrollbar. iOS7 introduces tableView:estimatedHeightForRowAtIndexPath: which if implemented allows you to just guess at the height before doing all the calculation. This can help out a lot on very large tables.
What I found works well is just have your tableView:heightForRowAtIndexPath: call cellForRowAtIndexPath: to get the cell for that row, and then query that cell for it's height cell.bounds.size.height and return that.
This works pretty well for small tables or in iOS7 with tableView:estimatedHeightForRowAtIndexPath implemented.
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.
I want to use auto-layout for UITableViewCells. These table cells have a dynamic height (depending on text length).
I'm using [UIView -systemLayoutSizeFittingSize:] to calculate the appropriate cell height (to return in [UITableView -heightForRowAtIndexPath:]) but I keep getting the following results:
If I pass UILayoutFittingCompressedSize, I get back a CGSize of (0,0).
If I pass UILayoutFittingExpandedSize, my app crashes with this error:
*** Assertion failure in -[NSISLinearExpression incrementConstant:], /SourceCache/Foundation_Sim/Foundation-1043.1/Layout.subproj/IncrementalSimplex/NSISLinearExpression.m:620
(My guess is that this means some number is infinite.)
My implementation is simple. I calculate the height for each object, and then cache it:
MessageCell *cell = // allocate a new cell...
// set up the cell (assign text, images, etc)
CGSize size = [cell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
self.cellHeight = size.height; // size always equals (0, 0)
I hypothesize that this is a problem with the constraints I set, but:
If I manually set cellHeight to a large value, the cells all look fine except the height is wrong.
Interface Builder gives me no warnings about ambiguous restraints
[cell hasAmbiguousLayout] returns NO.
My cell has, among other things, an image set at 48x48, so a size of (0, 0) shouldn't satisfy all the constraints.
Any ideas?
This is what works for me (note 'contentView')
CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
EDIT: I answered a similar question and provided a complete example, here:
How to resize superview to fit all subviews with autolayout?
It is hard to say something concrete basing on your post because you didn't post constraints that you use.
Apple Documentation:
systemLayoutSizeFittingSize:
Returns the size of the view that satisfies the constraints it holds.
Maybe you created constraints that can be interpreted in the way the size of the cell is equal to (0,0).
There is another way you can check the height of the cell with the text. You can put your text in the UITextView and then:
UITextView textView;
textView.text = #"l";
textView.font = [UIFont fontWithName:FONT_NAME size:FONT_SIZE];
//some code here
//
CGFloat cellHeight = textView.contentSize.height;
It is important to set the text and font (and every other property that can cause the change of the height of the UITextView) of the UITextView before using contentSize property. Also you must first add UITextView to the view.
////// EDIT
The problem with your approach with using constraints can be that you want to measure the cell which ISN'T added to the view so the system don't have all the informations it needs. It doesn't know how much space that will be for the space etc because it doesn't know the surrounding area of the cell
I want to learn a common and right way of calculation of height for custom cells.
My cells are loaded from nib, they have two multiline UILabels one above other.
At the moment I create special configuration cell in viewDidLoad and use it in heightForRowAtIndexPath.
-(CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
[self configureCell:self.configurationCell forIndexPath:indexPath];
CGRect configFrame = self.configurationCell.frame;
configFrame.size.width = self.view.frame.size.width;
self.configurationCell.frame = configFrame;
[self.configurationCell layoutSubviews];
UILabel *label = (UILabel *)[self.configurationCell viewWithTag:2];
float height = label.frame.origin.y + label.frame.size.height + 10;
return height;
}
It works but seems to be a bad manner. I think that heights must be precalculated for each item and for each orientation. But I can't find a way to make it nice and straightforward.
Could you help me?
Cell Label's style (like font and offsets from the screen borders) must be loaded from nib file (cell is inside nib).
Added cell's layourSubviews method:
-(void) layoutSubviews {
[super layoutSubviews];
[self.shortDescriptionLabel resize];
CGRect longDescriptionFrame = self.longDescriptionLabel.frame;
longDescriptionFrame.origin.y = self.shortDescriptionLabel.frame.origin.y + self.shortDescriptionLabel.frame.size.height + 5;
self.longDescriptionLabel.frame = longDescriptionFrame;
[self.longDescriptionLabel resize];
}
resize method of label simply increases it's height to fit all the text. So height of cell is calculated as longDescriptionLabel.bottom + 10. Simple code but not very beautiful.
It appears that you are trying to create subviews inside heightForRowAtIndexPath. View creation is supposed to be done in cellForRowAtIndexPath.
According to your implementation, you can only determine the height of the cell after it's been laid out. This is no good because UITableView calls heightForRowAtIndexPath for every cell, not just the visible ones upon data reload. As a result, subviews of all cells are created even if they aren't required to be visible.
To optimize this implementation, you have to work out some kind of formula to allow determination of height without laying out views. Even if your layout is elastic or has variable height, given text rectangle, you can still determine its height. Use [NSString sizeWithFont:] to determine its displayed rectangle. Record this information in your data delegate. When heightForRowAtIndexPath is called, return it directly.