12

I create a UITextView, add text to it, and put it in the view (with a container)

UITextView *lyricView = [[UITextView alloc] initWithFrame:screen];
lyricView.text = [NSString stringWithFormat:@"\n\n%@\n\n\n\n\n\n", lyrics];
[container addSubview:lyricView];
[self.view addSubview:container];

I then get the size of it for use with a button and add it to the UITextView

CGRect size = [lyrics boundingRectWithSize:CGSizeMake(lyricView.frame.size.width, MAXFLOAT)
                                 options:NSStringDrawingUsesLineFragmentOrigin
                              attributes:@{NSFontAttributeName:[lyricView font]}
                                 context:nil];
UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[doneButton setFrame:CGRectMake(56, size.size.height + 55, 208, 44)];
[doneButton setTitle:@"Done" forState:UIControlStateNormal];
[lyricView addSubview:doneButton];

This works in most cases. This will respect \n line breaks (like I added in my stringWithFormat) but it will not respect word wraps automatically added by the text view. So if lyrics has a line that doesn't fit on the screen, the UITextView will wrap it (as it's supposed to), but size is now slightly shorter than it should be because it did not respect the text view wrap.

vqdave
  • 2,361
  • 1
  • 18
  • 36
  • 1
    have you tried replace `NSStringDrawingUsesLineFragmentOrigin `by `NSStringDrawingUsesFontLeading`?which **Uses the font leading for calculating line heights** – johnMa Dec 16 '13 at 05:19
  • @johnMa When I use `NSStringDrawingUsesFontLeading`, it returns 0. When I use `NSStringDrawingUsesLineFragmentOrigin` or both, I have the issue described above. – vqdave Dec 16 '13 at 23:24
  • Any update on this? I am having the same problem and can't seem to find a good answer. – Barlow Tucker Mar 14 '14 at 22:46
  • @BarlowTucker Unfortunately, no. Please let me know if you find one! – vqdave Mar 16 '14 at 01:16
  • @BarlowTucker I found a solution! Check below for the code – vqdave Mar 29 '14 at 14:33

3 Answers3

7

You can tell boundingRectWithSize to process the string in word-wrapping mode. You have to add an NSParagraphStyle attribute to the attributes parameter, with its lineBreakMode set to NSLineBreakByWordWrapping. So:

NSMutableDictionary *attr = [NSMutableDictionary dictionary];     
// ...whatever other attributes you need...
NSMutableParagraphStyle *paraStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
paraStyle.lineBreakMode = NSLineBreakByWordWrapping;
[attr setObject:paraStyle forKey:NSParagraphStyleAttributeName];

then use attr as the attributes argument to boundingRectWithSize.

You can easily extend/generalise this technique to read other attributes including existing paragraph style attributes from whatever source makes sense.

user2067021
  • 4,399
  • 37
  • 44
JulianSymes
  • 2,135
  • 22
  • 24
  • 2
    In fact this doesn't work for me too! In some cases a single word is broken into two parts and placed in two lines... – LaborEtArs Mar 14 '18 at 15:44
  • I'm having the opposite problem, where boundingRectWithSize is measuring the text after wrapping the lines, and I want the unwrapped size. So I set the paragraph style to NSLineBreakByClipping and now I'm getting the results I wanted. – arlomedia Apr 04 '18 at 22:57
  • This solved the issue where width was smaller than it can be! Thx! – coolcool1994 Feb 21 '21 at 20:02
7

Should use (NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading) for options parameter.

samthui7
  • 923
  • 2
  • 12
  • 11
3

Did some more research and ended up finding this.

CGSize textSize = [textView sizeThatFits:CGSizeMake(textView.frame.size.width, FLT_MAX)];
CGFloat textHeight = textSize.height;

Hope this helps someone out there!

Community
  • 1
  • 1
vqdave
  • 2,361
  • 1
  • 18
  • 36