Solved

Getting text to fit inside UITextView

Posted on 2011-02-24
6
9,992 Views
Last Modified: 2013-11-25
Hi, I'm trying to dynamically change the size of text so that when it is displayed in a UITextView it entirely fits. My UITextView size cannot change but I want to display the text to be the largest size that can to fit inside the view. I have the following code:

- (IBAction) displayText:(id)sender
{
      
      BOOL run=TRUE;
      CGSize sizeOfText;
      int intFontSize =  [fontSize.text intValue];
      CGSize txtViewSize = self.textView.frame.size;
      CGSize txtViewAdjustedSize = CGSizeMake(self.textView.frame.size.width-16, self.textView.frame.size.height-16);  // 16 is assumed scrollbar size
      
      NSString *stringText = [[NSString alloc] initWithString:labelTextView.text];
      
      while(run){
            
            sizeOfText = [stringText sizeWithFont:[UIFont boldSystemFontOfSize:intFontSize] constrainedToSize:txtViewAdjustedSize lineBreakMode:UILineBreakModeWordWrap];
            
            NSLog(@"sizeOfText %@, fontSize %d, txtViewSize %@, txtViewAdjustedSize %@", NSStringFromCGSize(sizeOfText), (int)intFontSize, NSStringFromCGSize(txtViewSize), NSStringFromCGSize(txtViewAdjustedSize));
            
            //if(size.width<=txtViewWidth || size.height <= txtViewHeight )
            if(sizeOfText.width <= txtViewAdjustedSize.width && sizeOfText.height <= txtViewAdjustedSize.height)
                  run = FALSE;
            else
                  intFontSize--;
      }
      
      self.textView.font = [UIFont boldSystemFontOfSize:intFontSize];
      self.textView.text = [NSString stringWithFormat:@"%@", stringText];
}


This displays the text but it is truncated until it gets down to font size 16 when it actually goes through the while loop to make it font size 15 which actually does fit in. Please see below log output:

sizeOfText {153, 57}, fontSize 48, txtViewSize {176, 114}, txtViewAdjustedSize {160, 98}

sizeOfText {129, 86}, fontSize 36, txtViewSize {176, 114}, txtViewAdjustedSize {160, 98}

sizeOfText {128, 87}, fontSize 24, txtViewSize {176, 114}, txtViewAdjustedSize {160, 98}

sizeOfText {157, 88}, fontSize 18, txtViewSize {176, 114}, txtViewAdjustedSize {160, 98}

sizeOfText {159, 100}, fontSize 16, txtViewSize {176, 114}, txtViewAdjustedSize {160, 98}

sizeOfText {153, 95}, fontSize 15, txtViewSize {176, 114}, txtViewAdjustedSize {160, 98}

I'm confused by the fontSize. It is width and height isn't it. Why is fontSize 48 = 153, 57 and fontSize 16 = 159,100?

. What am I doing wrong?  I have the project code available if you need it. Cheers.     iPhone Simulator Image
0
Comment
Question by:englishchrissy
  • 4
  • 2
6 Comments
 
LVL 5

Expert Comment

by:mad_mac
ID: 34978614
is there any reason you are using a UITextView...?

you could simply override you view draw method and use the NSString drawing methods.

use the sizeWithFont methods to calculate the correct font size and then on of drawInRect methods to actually draw it.

obviously this is only any good if you want to simply display text to the user, if you want them to be able to interact with the text then this will not work

http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/TextLayout/TextLayout.html

The drawing strings section of the above document should help.
0
 

Author Comment

by:englishchrissy
ID: 34978994
I use the UITextView only to display the text. It cannot be edited and will only ever be one sentence but it must wrap as the text won't fit on one line. I am new to obj-c so maybe I don't need a UITextView. What else could I use and would that make any difference to what I'm trying to achieve.

The UITextView area above (after adjusting for scroll bars) is (160,98). All I'm trying to do above is see if the text fits that area and if it doesn't then I reduce the size of the font until it does. Sounds simple in theory.

Can you explain why I'm getting the sizeOfText for fontSize 48 is (153, 57). Whereas it is (159,100) for fontSize 16. Surely 48 is bigger than 16. Am I doing something stupid?

When I run the app with fontSize = 16 it goes through the while loop twice and reduces the fontSize to 15 where the text does actually fit inside the UITextView. Why is fontSize 48 size smaller than fontSize 16?


0
 
LVL 5

Accepted Solution

by:
mad_mac earned 500 total points
ID: 34980185
the issue here is you are not fully understanding what "sizeWithFont:constrainedToSize:lineBreakMode:" is doing.  It is making assumptions about the truncation of the display string, therefore yes at 48pt a smaller CGSize is returned but if you were to render the text using "drawInRect:withFont:lineBreakMode:" it would simply truncate a significant amount of the text away.

from the description of  sizeWithFont:constrainedToSize:lineBreakMode:

If the receiver’s text does not completely fit in the specified size, it lays out as much of the text as possible and truncates it (for layout purposes only) according to the specified line break mode. It then returns the size of the resulting truncated string.

which is why these days i tend to use either the the drawing extensions of NSString or use core text to draw strings where i can predicts the exact behaviour.

If you were to use something like this to look at the size needed to place the string it may become more apparent what is going on..
NSString *testText = @"This is the text that I want to fit into a UITextView so that it does not get truncated!";
CGSize maxSize = CGSizeMake(160, 100);
UIFont *tmpFont = [UIFont systemFontOfSize:10.0];

for (int i=48; i > 9; i--) {
	
	CTFontRef fnt = CTFontCreateWithName( (CFStringRef)tmpFont.fontName, i, NULL);
	CFMutableAttributedStringRef theText = CFAttributedStringCreateMutable(NULL, 0);
	CFAttributedStringBeginEditing(theText);
	CFAttributedStringReplaceString (theText, CFRangeMake(0,0), (CFStringRef)testText);
	CFAttributedStringSetAttribute(theText, CFRangeMake(0,[testText length]), kCTFontAttributeName, fnt);
	CFAttributedStringEndEditing(theText);
	
	CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString(theText);
	CFRange fitRange;
	CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints( frameSetter, CFRangeMake(0,[testText length]), NULL, maxSize, &fitRange);

	NSLog(@"sizeOfText %@, fontSize %d, maxSize %@, rangefit {%d,%d}", 
		  NSStringFromCGSize(textSize), 
		  (int)i, 
		  NSStringFromCGSize(maxSize), 
		  fitRange.location, 
		  fitRange.length);
	
	CFRelease (theText);
	CFRelease (frameSetter);
}

Open in new window


The output of this is something like below
sizeOfText {152.016, 57.6}, fontSize 48, maxSize {160, 100}, rangefit {0,8}
sizeOfText {148.849, 56.4}, fontSize 47, maxSize {160, 100}, rangefit {0,8}
sizeOfText {145.682, 55.2}, fontSize 46, maxSize {160, 100}, rangefit {0,8}
sizeOfText {142.515, 54}, fontSize 45, maxSize {160, 100}, rangefit {0,8}
sizeOfText {139.348, 52.8}, fontSize 44, maxSize {160, 100}, rangefit {0,8}
sizeOfText {136.181, 51.6}, fontSize 43, maxSize {160, 100}, rangefit {0,8}
sizeOfText {133.014, 50.4}, fontSize 42, maxSize {160, 100}, rangefit {0,8}
sizeOfText {145.862, 98.4}, fontSize 41, maxSize {160, 100}, rangefit {0,17}
sizeOfText {142.305, 96}, fontSize 40, maxSize {160, 100}, rangefit {0,17}
sizeOfText {138.747, 93.6}, fontSize 39, maxSize {160, 100}, rangefit {0,17}
sizeOfText {135.189, 91.2}, fontSize 38, maxSize {160, 100}, rangefit {0,17}
sizeOfText {131.632, 88.8}, fontSize 37, maxSize {160, 100}, rangefit {0,17}
sizeOfText {128.074, 86.4}, fontSize 36, maxSize {160, 100}, rangefit {0,17}
sizeOfText {169.224, 84}, fontSize 35, maxSize {160, 100}, rangefit {0,24}
sizeOfText {164.389, 81.6}, fontSize 34, maxSize {160, 100}, rangefit {0,24}
sizeOfText {159.554, 79.2}, fontSize 33, maxSize {160, 100}, rangefit {0,24}
sizeOfText {154.719, 76.8}, fontSize 32, maxSize {160, 100}, rangefit {0,24}
sizeOfText {149.884, 74.4}, fontSize 31, maxSize {160, 100}, rangefit {0,24}
sizeOfText {145.049, 72}, fontSize 30, maxSize {160, 100}, rangefit {0,24}
sizeOfText {140.214, 69.6}, fontSize 29, maxSize {160, 100}, rangefit {0,24}
sizeOfText {135.379, 67.2}, fontSize 28, maxSize {160, 100}, rangefit {0,24}
sizeOfText {130.544, 97.2}, fontSize 27, maxSize {160, 100}, rangefit {0,36}
sizeOfText {125.709, 93.6}, fontSize 26, maxSize {160, 100}, rangefit {0,36}
sizeOfText {161.194, 90}, fontSize 25, maxSize {160, 100}, rangefit {0,41}
sizeOfText {161.391, 86.4}, fontSize 24, maxSize {160, 100}, rangefit {0,43}
sizeOfText {162.359, 82.8}, fontSize 23, maxSize {160, 100}, rangefit {0,43}
sizeOfText {155.3, 79.2}, fontSize 22, maxSize {160, 100}, rangefit {0,43}
sizeOfText {148.241, 75.6}, fontSize 21, maxSize {160, 100}, rangefit {0,43}
sizeOfText {163.438, 96}, fontSize 20, maxSize {160, 100}, rangefit {0,74}
sizeOfText {164.738, 91.2}, fontSize 19, maxSize {160, 100}, rangefit {0,78}
sizeOfText {156.067, 86.4}, fontSize 18, maxSize {160, 100}, rangefit {0,78}
sizeOfText {158.429, 81.6}, fontSize 17, maxSize {160, 100}, rangefit {0,78}
sizeOfText {149.109, 96}, fontSize 16, maxSize {160, 100}, rangefit {0,88}
sizeOfText {153.442, 72}, fontSize 15, maxSize {160, 100}, rangefit {0,88}
sizeOfText {162.627, 67.2}, fontSize 14, maxSize {160, 100}, rangefit {0,88}
sizeOfText {151.011, 62.4}, fontSize 13, maxSize {160, 100}, rangefit {0,88}
sizeOfText {157.189, 43.2}, fontSize 12, maxSize {160, 100}, rangefit {0,88}
sizeOfText {159.387, 39.6}, fontSize 11, maxSize {160, 100}, rangefit {0,88}
sizeOfText {159.355, 36}, fontSize 10, maxSize {160, 100}, rangefit {0,88}

Open in new window


as you can see it is not until you get down to a text size of 16 or below does the entire string fit.

i hope this helps, programming guide is a good starting place for it
0
Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

 

Author Comment

by:englishchrissy
ID: 34981182
Thanks for the help mad mac, it's very much appreciated. I have replaced my UITextViews with labels as you suggested. I will also implement your other suggestions. It has certainly pointed me in the right direction. I did not know how complicated working with fonts can be. I was expecting it to be easy but have had a look at the Apple docs and there is certainly a lot to it.
0
 

Author Comment

by:englishchrissy
ID: 34986650
Ok, this is my re-worked example. In the app I'm coding the font size will never be more than 22 and I've shortened the string here because it will never be that long. So this now corresponds to what my app actually needs. I've added a bit of code to see if the fitRange < stringLength and if it is I subtract 4 from the font size. A bit of a bodge, I know but it works.

If anyone has advice or improvements I would love to hear. I'm sure there are many. If anyone wants the project source code, just contact me.

- (IBAction) displayText:(id)sender
{
      
      BOOL run = TRUE;
      NSString *testText = [[NSString alloc] initWithString:labelTextToBeDisplayed.text];

      int stringLength = [testText length];
      CGSize maxSize = self.labelNewText.frame.size;
      UIFont *tmpFont = [UIFont boldSystemFontOfSize:22.0];
      int i = [fontSize.text intValue];
      int fitRangeLength = 0;
      
      while(run)
      {
            
            CTFontRef fnt = CTFontCreateWithName( (CFStringRef)tmpFont.fontName, i, NULL);
            CFMutableAttributedStringRef theText = CFAttributedStringCreateMutable(NULL, 0);
            CFAttributedStringBeginEditing(theText);
            CFAttributedStringReplaceString (theText, CFRangeMake(0,0), (CFStringRef)testText);
            CFAttributedStringSetAttribute(theText, CFRangeMake(0,[testText length]), kCTFontAttributeName, fnt);
            CFAttributedStringEndEditing(theText);
            
            CTFramesetterRef frameSetter = CTFramesetterCreateWithAttributedString(theText);
            CFRange fitRange;
            CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints( frameSetter, CFRangeMake(0,[testText length]), NULL, maxSize, &fitRange);
            
            NSLog(@"sizeOfText %@, fontSize %d, maxSize %@, rangefit {%d,%d}, stringLength %d",
                              NSStringFromCGSize(textSize),
                              (int)i,
                              NSStringFromCGSize(maxSize),
                              fitRange.location,
                              fitRange.length,
                              stringLength);
            
            CFRelease (theText);
            CFRelease (frameSetter);
            fitRangeLength = (int)fitRange.length;
            
            float floatWidth = textSize.width;
            float floatHeight = textSize.height;
            int intWidth = (int)(floatWidth + 0.5);
            int intHeight = (int)(floatHeight + 0.5);
            
            if(intWidth <= maxSize.width && intHeight <= maxSize.height)
            {
                  run = FALSE;                  
            }
            else
            {                  
                  i--;
            }
      }
      if (fitRangeLength < stringLength) {
             i = i - 4;
            NSLog(@"i = %d", (int)i);
      }
      
      self.labelNewText.font = [UIFont boldSystemFontOfSize:i];
      self.labelNewText.text = [NSString stringWithFormat:@"%@", testText];
      [testText release];

}

 iPhone App Screenshot
0
 

Author Closing Comment

by:englishchrissy
ID: 35018635
It was enough to point me in the right direection. I would have been lost without mad mac's help
0

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This tutorial is posted by Aaron Wojnowski, administrator at SDKExpert.net.  To view more iPhone tutorials, visit www.sdkexpert.net. This is a very simple tutorial on finding the user's current location easily. In this tutorial, you will learn ho…
CocoaPods is the best way to manage library dependencies in iOS and OS X projects. By using cocoa pods there is no need of downloading the code from github and copying to your project. There are plenty of open source libraries now available with C…
The goal of this video is to provide viewers with basic examples to understand and use structures in the C programming language.
The goal of this video is to provide viewers with basic examples to understand how to use strings and some functions related to them in the C programming language.

820 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question