Link to home
Start Free TrialLog in
Avatar of englishchrissy
englishchrissy

asked on

Getting text to fit inside UITextView

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.     User generated image
Avatar of mad_mac
mad_mac

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.
Avatar of englishchrissy

ASKER

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?


ASKER CERTIFIED SOLUTION
Avatar of mad_mac
mad_mac

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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.
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];

}

 User generated image
It was enough to point me in the right direection. I would have been lost without mad mac's help