This question already has answers here:
Calling heightForRowAtIndexPath from within cellForRowAtIndexPath?
(3 answers)
Closed 10 years ago.
If I'm making a custom UITableViewCell and I need it to have a variable height depending on the length of the input (for instance, for a Twitter reader or something), how can I make this work?
I've found lots of other examples here that can set it for a the standard, non-custom cell, but my cell's main text label is smaller, etc., so when I try to use any of those methods on my cell, they give a variety of weird results (text overlapping the bottom of the cell, etc.)
Is there a standardized way of designing the cell (for example, how tall should I make it in Interface Builder?), and let's say my label was half the width of that cell.. how would I go about calculating the height the cell would need to be to display the string loaded into that label? Here's a method that I found here which works fine on the normal cell, but screws up custom ones with weird heights, overlapping text, etc: (I have absolutely NO idea what the 300 / 200000 do here, if anyone could explain that I'd be grateful, too!)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGSize textSize = {300.f, 200000.0f};
CGSize size = [[_quoteStringsFromPlist objectAtIndex: indexPath.row] sizeWithFont: [UIFont systemFontOfSize:12.0f] constrainedToSize: textSize lineBreakMode: NSLineBreakByWordWrapping];
size.height += 5.0f;
float result = MAX(size.height, 32.0f);
return result;
}
Something like this should work.
CGRect labelFrame = // enter default frame of the label here
UIFont *labelFont = // enter label font here
CGFloat labelBottomMargin = // enter the space between the bottom of the label and the bottom of the cell here
CGSize size = [labelString sizeWithFont:labelFont constrainedToSize:CGSizeMake(labelFrame.size.width, MAX_FLOAT)];
size.height += labeFrame.origin.y;
size.height += labelBottomMargin;
size.height = MAX(size.height, 32.0f);
return size.height;
First, head over to your xib file and see how large are the top/bottom margins of your text area, and how wide it is.
After that, you need to calculate the height needed for it with the width you have available, and then add that value to the top/bottom margins your text area already has.
The result should look correctly regardless of the size of each cell in IB or the text you are trying to put in them.
EDIT
Imagine your cell size is {700, 300} in your IB, and your text area is located on {{100, 100}, {300,100}}, your text area has a 100px margin top and 100px margin bot, and it's width is 300.
When you calculate the height you require for your text, you calculate it with an available 300 width. It will return a size, something like {300, 250}. That 250 is the height required by your text, but your cell has other stuff in it and you need to add those top and bot margins to that, so the result is 450.
Remember to set an autoresizing mask or autolayout so that your text area stretches vertically and the margins are fixed (UIViewAutoresizingFlexibleHeight)
Related
I'm building a list view with self-sizing in my app using UICollectionView. Getting UICollectionViewFlowLayout to do a vertical list layout is a pain, so I'm writing my own UICollectionViewLayout subclass to do it.
My cells look a lot like regular table view cells - an image view on the left, a couple of labels vertically stacked in the center, and an accessory view on the right. The labels can wrap to a few lines and grow in font size according to the system font size setting, which is why they need to be self-sizing. Constraints are sensible - left to image view, image view to label, label to accessory, accessory to right, label to top and bottom.
To size my cells via preferredLayoutAttributesFittingAttributes, I need to get the desired height of the cell given the width of the collection view. I'd expect to use systemLayoutSizeFittingSize:horizontalPriority:verticalPriority: for this, passing a size like {desiredWidth, crazyLargeHeight}, UILayoutPriorityRequired for horizontal priority, and 1 for vertical priority. But the size I get back from systemLayoutFittingSize isn't sensible. The width is some previous width value from the cell (which doesn't necessarily match self.bounds.size.width or the passed-in layoutAttributes' size.width), and a height that works with that width. I also have tried passing a small height instead of a large one, and it still doesn't return the size it actually needs.
So how do I get the preferred height for the cell given a width value?
Sample code:
-(UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
UICollectionViewLayoutAttributes *result = [super preferredLayoutAttributesFittingAttributes:layoutAttributes];
CGSize exampleSize = CGSizeMake(layoutAttributes.size.width, 20);
CGSize size = [self systemLayoutSizeFittingSize:exampleSize withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:1];
result.size = size;
return result;
}
Turns out if you ask the cell's content view for a size instead of the cell itself, it works. And you have to give a sample height that is smaller than will be needed.
-(UICollectionViewLayoutAttributes *)preferredLayoutAttributesFittingAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes
{
UICollectionViewLayoutAttributes *result = [super preferredLayoutAttributesFittingAttributes:layoutAttributes];
CGSize exampleSize = CGSizeMake(layoutAttributes.size.width, 20); //magic number for example purposes...my cell will definitely be taller
CGSize size = [self.contentView systemLayoutSizeFittingSize:exampleSize withHorizontalFittingPriority:UILayoutPriorityRequired verticalFittingPriority:1];
result.size = size;
return result;
}
I'm guessing that the content view isn't pinned to the cell's bounds the way I expected. And this exact implementation will likely fall down if the content view is inset for some reason. But the key to solving this was the fact that having the cell contents pinned to the cell's content view does not mean that the cell itself will compute a size as you expect.
I am working on an ios application, and using autolayout I am trying to create a Table View with different row heights.
The layout of the prototype cell is as follows:
I have the main cell (in black) inside it I have a UIView (in red) and inside that view a UILabel (in blue)
The Autolayout constraints I added are as shown on the figure:
The UIView has the following Constraints:
80 to the left edge of the cell
20 from the right edge of the cell
15 from the top edge of the cell
15 from the bottom edge of the cell
The UILabel has the following Constraints:
20 to the left edge of the UIView
15 from the right edge of the UIView
10 from the top edge of the UIView
10 from the bottom edge of the UIView
I need the UILabel to be dynamic in height based on the text size inside of it.
To do that, I have done the following:
Set the number of lines of the UILabel to 0
Set the font to Helvetica Neueu with size 15 (in the interface builder)
Set the Lines Break to "Word wrap" (in the interface builder)
in the view Controller, I have implemented the following:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
//get the item
ListItem *item = (ListItem*) self.items[indexPath.row];
//calculate the label size based on the item title that we will display
CGSize textSize = [item.title sizeWithFont:[UIFont fontWithName:#"HelveticaNeue" size:15.0] constrainedToSize:CGSizeMake(tableView.frame.size.width - 135.f, 9999.f) lineBreakMode:NSLineBreakByWordWrapping];
//return the height + the 50 to accomodate with the layout
return textSize.height + 50.f;
}
Basically what I'm doing is getting the text I need to display (item.title) and I call sizeWithFont to calculate how much I need space for that label and return it.
in the sizeWithFont method I pass the font I am using for the title [UIFont fontWithName:#"HelveticaNeue" size:15.0] and then I constraint the size of the calculation based on the pictures constraints, by getting the width of the tableView and subtracting the margins to get to the label
For the width: I substract 80 (for the UIView left) and 20 (for the label left) and 15 (for the label right) and 20 for the UIView Right)
For the Height I put 9999 as I don't need a constraint on it for the calculations.
After I get the size of the label needed I returned the exact height plus the 15, 10 ,10 and 15 for the vertical margins of the label (total = 50) return textSize.height + 50.f;
The problem:
Although I am doing exact calculation, but when running the app it is not 100% precise. While most cases I get a precise height, but in some cases (especially when we have for example 3 lines in the label, and the 3rd line has one one word), the cell get a height corresponding to 2 lines only and cutting the third one. increasing the height by trial and error might be possible but it will also affect the height of the cells that were displayed well.
So my question is, what am I doing wrong with my calculations? and is there a way to have a dynamic height for the cells based on the text in the label, when using autolayout?
Thanks
Any chance the fonts don't match?
You'd be calculating based on Helvetica Neue Regular, I think.
What's the font of the UILabel? Being off by one word makes me think font metrics.
I don't see another error
If you want precise, use the prototype cell approach. It is inherently precise because you're getting the height of an actual cell.
Using this approach for dynamic label height generally involves setting the label's text, calling sizeToFit and then getting the label's intrinsicContentSize. Here is an example implementation.
I have a UITableViewCell subclass that contains a UITextView where scrolling is turned off. I set its frame like this, in my table view cell layoutSubviews method:
CGRect frame = self.bodyTextView.frame;
CGSize size = self.bodyTextView.contentSize;
frame.size = size;
self.bodyTextView.frame = frame;
This has been working fine for some time, but I've noticed that in a case where I have a particularly large amount of text, the text is getting cut off. I've set the text view frame background color to orange so I could verify that the frame was being set correctly. Here is an example (I am only showing the bottom portion of the text view):
The frame is the correct size based on the text (in this case 1019 points), but the text stops before the bottom of the text view. I have also seen the text get cut off part way through a line, (ie the text of the last visible line of text is cut off half way through horizontally). Does anyone have an idea what is happening here?
A few other points:
The text views work well for all my table view cells with shorter content.
If I increase the amount of text in the case shown above, the text view height increases, but the text still gets cut off at the same place.
According to this and similar answers, the problem could be that "the correct contentSize is only available after the UITextView has been added to the view ... Prior to that it is equal to frame.size"
I'd suggest you to calculate the height in a different way, like -sizeWithFont: or -sizeTofit
Use this to get the textSize
// find the text width; so that btn width can be calculate
CGSize textSize = [#"YOUR TEXT" sizeWithFont:[UIFont fontWithName:#"Arial" size:20.0]
constrainedToSize:CGSizeMake(320.0f, 99999.0f)
lineBreakMode:UILineBreakModeWordWrap];
and then set height using this as :
CGRect frame = self.bodyTextView.frame;
frame.size = textSize;
self.bodyTextView.frame = frame;
Hope it helps you.
I had the same issue and resolved it working on the textView padding (in swift5):
yourTextView.textContainerInset = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
Hope it can help :)
I know this is a very common issue here on stack overflow, however, i haven't been able to figure out what is going on by looking at the various threads on the topic.
I've got a nib file with a UITableViewCell subclass and a UILabel in the cell. The desired behavior is for the UILabel to expand vertically and display multiple lines of text (if the text is long enough). I've set the number of lines property in interface builder to 0, and I've also set the line breaks to word wrap (also in interface builder). When I display the cell, I am setting the height of the cell to be a function of sizeWithFont: constrainedToSize: linebreakmode: using the text of the cell. The height is returning correctly for the cell, however the UILabel does not wrap, nor does it expand vertically.
Any idea what might be going wrong?
Thanks!
**UPDATE
Here is the code I used to calculate the height of the cell:
+ (CGFloat)heightForCellGiveText:(NSString *)text
{
CGSize size = [text sizeWithFont:[self defaultMainFont] constrainedToSize:CGSizeMake(LABEL_WIDTH, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];
return size.height + (2 * VERT_PADDING) + INTER_LABEL_PADDING;
}
I was hoping to let interface builder automatically handle everything having to do with actually resizing the label (and not the cell)
I'm trying to place two UILabel on the same vertical axis within a UITableViewCell, and have them take up 50% of the cell's width, when the cell is a grouped table cell.
I've tried self.frame.size.width / 2, but the width that self.frame.size.width looks like the width of the entire cell as if it were not a grouped cell. In other words, it seems to be the width of the whole screen, in a full screen table.
I also compared self.frame.size.width to self.contentView.frame.size.width and both appear to be the same, and are not the actual width of the grouped cell, I don't believe.
When I initialize the UILabel with a specified frame, and specify the x-axis to as 0, the label does place itself on the left edge of the grouped cell, which is great! That's what I want.
But, for the 2nd UILabel that I wish to be to the right of the 1st label, on the same horizontal axis, I do not know how to calculate where to start for the label's frame's x-axis.
Likewise, I do not know how to calculate width for either of these labels, because I can't seem to get the width of the grouped cell.
I feel like there must be a way to get the exact width, because for the first label, giving the label's frame an x-axis of 0 puts it right on the edge of the cell, which is exactly as i need. So I need like internally, this code know the width of that cell too, somewhere.
Ok, I figured out a good way to achieve this. If you're subclassing UITableViewCell and want to adjust the cell's sub-views, then you need to perform these calculations and frame adjustments inside of the layoutSubviews: method. Inside of this method, the cell's contentView will be the intended width, even when using a grouped cell. Below is what my layoutSubviews function looks like ...
- (void)layoutSubviews {
[super layoutSubviews];
// half of the cell's content view width
CGFloat halfCellWidth = self.contentView.frame.size.width / 2;
// the dimensions of the message label, primarily for the height
CGSize mesageLabelDimensions = [_message.text sizeWithFont:[UIFont systemFontOfSize:12]
constrainedToSize:CGSizeMake(self.contentView.frame.size.width - (LEFT_RIGHT_MARGIN * 2), 500.0)];
// adjust the frames of the cell labels
_source.frame = CGRectMake(LEFT_RIGHT_MARGIN, TOP_BOTTOM_MARGIN, halfCellWidth - LEFT_RIGHT_MARGIN, LABEL_LINE_HEIGHT);
_timestamp.frame = CGRectMake(halfCellWidth, TOP_BOTTOM_MARGIN, halfCellWidth - LEFT_RIGHT_MARGIN, LABEL_LINE_HEIGHT);
_message.frame = CGRectMake(LEFT_RIGHT_MARGIN, LABEL_LINE_HEIGHT + (TOP_BOTTOM_MARGIN * 2), mesageLabelDimensions.width, mesageLabelDimensions.height);
}