43

How would one modify the following snippet (in a tableView:cellForRowAtIndexPath: UITableViewController method) from the "09a - PrefsTable" recipe from Chapter 6 of The iPhone Developer's Cookbook:

if (row == 1) { 
    // Create a big word-wrapped UILabel 
    cell = [tableView dequeueReusableCellWithIdentifier:@"libertyCell"]; 
    if (!cell) { 
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"libertyCell"] autorelease]; 
        [cell addSubview:[[UILabel alloc] initWithFrame:CGRectMake(20.0f, 10.0f, 280.0f, 330.0f)]]; 
    } 
    UILabel *sv = [[cell subviews] lastObject]; 
    sv.text =  @"When in the Course of human events, it becomes necessary for one people to dissolve the political bands which have connected them with another, and to assume among the powers of the earth, the separate and equal station to which the Laws of Nature and of Nature's God entitle them, a decent respect to the opinions of mankind requires that they should declare the causes which impel them to the separation."; 
    sv.textAlignment = UITextAlignmentCenter; 
    sv.lineBreakMode = UILineBreakModeWordWrap; 
    sv.numberOfLines = 9999; 
    return cell; 
} 

...to size the "sv" UILabel subview and the "cell" UITableViewCell to be sized just big enough to fit the text (and work with more or less text, and other types of text alignment)?  I looked at the UILabel textRectForBounds:limitedToNumberOfLines: method, but the documentation states that it should not be called directly (and should only be overridden).  I experimented with the UIView sizeToFit method, without success.

Update: I asked a new question about my problem with the NSString -sizeWithFont:forWidth:lineBreakMode: method.

Community
  • 1
  • 1
Daryl Spitzer
  • 143,156
  • 76
  • 154
  • 173

9 Answers9

99

I had to do this enough that I extended UILabel to do it for me:

@interface UILabel (BPExtensions)
- (void)sizeToFitFixedWidth:(CGFloat)fixedWidth;
@end

@implementation UILabel (BPExtensions)


- (void)sizeToFitFixedWidth:(CGFloat)fixedWidth
{
    self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, fixedWidth, 0);
    self.lineBreakMode = NSLineBreakByWordWrapping;
    self.numberOfLines = 0;
    [self sizeToFit];
}
@end

then to have a label to have a variable multiline height but a fixed width just:

[myLabel sizeToFitFixedWidth:kSomeFixedWidth];

BadPirate
  • 25,802
  • 10
  • 92
  • 123
  • 3
    Magic. set the height of frame to 0 - then call [self sizeToFit]; Thx. – johndpope Mar 23 '12 at 01:46
  • Shouldn't it be better to set the bounds instead of the frame? – Anton Mar 27 '13 at 01:42
  • Bounds can work too, it's just a shift for frame. Since I set the x and the y to the self.frame.origin values, it accomplishes the same thing as self.bounds = CGRectMake(0,0,fixedWidth,fixedHeight), it would be nice if you could set the frame.size property directly, but this doesn't work :) – BadPirate Mar 29 '13 at 00:14
  • Thanks works great!, although for ios 6 there are other choices, but my app is iOS 5+ – Oscar Gomez May 06 '13 at 14:29
  • It seems that UILineBreakModeWordWrap is deprecated in iOS 6 already. What are other choices @OscarGomez ? – Paweł Brewczynski Mar 30 '14 at 19:11
  • @bluesm - http://stackoverflow.com/a/12931670/285694 - Functionality is there, just uses a different name :) – BadPirate Mar 31 '14 at 22:57
17

You should use NSString's -sizeWithFont:forWidth:lineBreakMode: method to retrieve the associated sizing metrics for your label.

Lily Ballard
  • 182,031
  • 33
  • 381
  • 347
10

Also, change the numberOfLines property to 0 if you're going to use that code.

Sophie Alpert
  • 139,698
  • 36
  • 220
  • 238
5

NSString's -sizeWithFont:forWidth:lineBreakMode: does not actually perform the word wrap. Instead, use -sizeWithFont:constrainedToSize:lineBreakMode: to get an accurate width AND height value for the string.

pix0r
  • 31,139
  • 18
  • 86
  • 102
  • Just saw the link to your other question. Looks like you got it right over there - sorry for the dupe. – pix0r Apr 15 '09 at 23:07
4

Try this:

sv.text =  @"When in the Course of human events, it becomes necessary for one people to dissolve the political bands which have connected them with another, and to assume among the powers of the earth, the separate and equal station to which the Laws of Nature and of Nature's God entitle them, a decent respect to the opinions of mankind requires that they should declare the causes which impel them to the separation."; 
sv.textAlignment = UITextAlignmentCenter; 
sv.lineBreakMode = UILineBreakModeWordWrap; 
sv.numberOfLines = 0;
[sv sizeToFit]; 
Sebastian
  • 2,889
  • 4
  • 34
  • 37
2

Also, you will need to implement the UITableViewDelegate method:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

And have it return a total cell height adjusted for the resized text field.

One other note - Size to Fit should actually work, if you have number of lines set to 0 as previously mentioned. It would give you back a size with the height increased to accomidate the word-wrapped text set in the label and the width set to whatever the original label width had.

That will not help you though as you need to get the size in heightForRow before the cell is obtained, so you are better off calculating the height needed (and very probably caching that calculation so as not to slow down table rendering)

Kendall Helmstetter Gelner
  • 74,769
  • 26
  • 128
  • 150
1

Here's a bit of code i use:

CGSize textSize = [myLabel.text sizeWithFont:myLabel.font];
Chris
  • 39,719
  • 45
  • 189
  • 235
0

I had similar problem, I had a UITableViewCell that was designed in StoryBoards as a static cell. I used [super tableView:cellForRowAtIndexPath:] to get it. So I wanted to resize the UILabel "detailTextLabel" so it fits the text I set to it. The style was "Right Detail".

I just set the text in my tableView:cellForRowAtIndexPath:. And than in tableView:heightForRowAtIndexPath: I returned

UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];
return cell.detailTextLabel.frame.size.height

I had long string. And finally had a wide Cell with 4 lines of text in label.

DanSkeel
  • 3,853
  • 35
  • 54
0

I had similar problem. I solved this.

In cellForRowAtIndexPath method set font size to whatever you want.

cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.numberOfLines = 0;    
[cell.textLabel setFont:[UIFont systemFontOfSize:14.0]];
[cell.textLabel sizeToFit];

And in heightForRowAtIndexPath method increase font size.

    CGFloat height;

    UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
    NSString *text = cell.detailTextLabel.text;
    CGSize constraint = CGSizeMake(320, 20000.0f);
    CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:20.0]     constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];
    CGFloat calHeight = MAX(size.height, 44.0f);
    height =  calHeight + (CELL_CONTENT_MARGIN * 2);

    return height;