BoundingRectWithSize does not replicate UITextView

My requirement in the project is that the font size of the UITextView should be reduced in accordance with the contents of the UITextView . Therefore, I am trying to estimate the size of the text using boundingRectWithSize .

The problem is that the font size that I get is too large, and part of the text is cropped.

My function:

  -(BOOL)updateTextViewFontSizeForText:(NSString*)text{ float fontSize = self.maximumFontSizeInPoints; self.font = [self.font fontWithSize:fontSize]; CGSize tallerSize ; CGSize stringSize ; do { if (fontSize <= self.minimumFontSizeInPoints) // it just won't fit return NO; fontSize -= 1.0; self.font = [self.font fontWithSize:fontSize]; NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; [paragraphStyle setLineBreakMode:NSLineBreakByWordWrapping]; NSDictionary *attributes = @{ NSFontAttributeName: self.font, NSParagraphStyleAttributeName : paragraphStyle }; tallerSize = CGSizeMake(self.frame.size.width,self.frame.size.height-16);// the 16 is given because uitextview adds some offset stringSize = [text boundingRectWithSize:CGSizeMake(self.contentSize.width,CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:attributes context:nil].size; }while(stringSize.height >= tallerSize.height); if ([self.onTextChangDelegate respondsToSelector:@selector(onTextChangDelegate)]) { [self.onTextChangDelegate onTextChanged:text]; } return YES; } 
+3
ios objective-c ios7
source share
3 answers

I encountered the same problem when trying to do the same.

The problem is how the UITextView trigger line breaks compared to boundingRectWithSize. You can read more details here: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TextLayout/Concepts/CalcTextLayout.html

But you can calculate the exact size! Basically, there are two UITextView properties that need to be considered in order to get the correct size estimates. The first is textContainer.lineFragmentPadding , the second is textContainerInset .

Firstly, textContainer.lineFragmentPadding : You may have noticed that your size is usually disabled by 10px , because the default value for the system is 5px . When you calculate your estimated size, you need to subtract this value from the size you are checking for and add it back when you have the final value.

Secondly, textContainerInset . This is the UIEdgeInset that you will need to add to your final calculated value according to the systems.

This is code based on how I solved the problem:

 - (CGSize)sizeThatFits:(CGSize)size CGFloat lineFragmentPaddings = self.textContainer.lineFragmentPadding * 2; CGFloat horzPadding = self.textContainerInset.left + self.textContainerInset.right + lineFragmentPaddings; CGFloat vertPadding = self.textContainerInset.top + self.textContainerInset.bottom; size.width -= horzPadding; CGRect boundingRect = [attributedText boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin context:nil]; size = boundingRect.size; // I found through debugging that adding 0.25 rounded // matches sizeThatFits: identically. Not sure why… size.width += horzPadding + 0.25; size.height += vertPadding + 0.25; size = CGSizeRound(size); return size; } 

Note. CGSizeRound is just a custom function I wrote that rounds width and height from CGSize to the nearest 0.5 .

For comparison, if you create a second UITextView and make sure that the values ​​of textContainer.lineFragmentPadding and textContainerInset same, you should see values ​​that are almost identical to the nearest 0.5 .

And to your question about calculating the correct pointSize, this is some pseudo code for this:

 CGFloat pointSize = 64; CGFloat minPointSize = 32; CGFloat decrementor = 4; CGFloat padding = self.textContainerInset.left + self.textContainerInset.right + lineFragmentPaddings; CGFloat actualWidth = self.maxTextViewSize.width - padding * 2; CGRect boundingRect = CGRectZero; BOOL isValidPointSize = NO; do { if (pointSize < minPointSize) { pointSize = minPointSize; boundingRect.size.height = self.maxTextViewSize.height; isValidPointSize = YES; } else { NSDictionary *defaultAttributes = [self.customTextStorage defaultAttributesForPointSize:pointSize]; NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:string attributes:defaultAttributes]; boundingRect = [attrString boundingRectWithSize:CGSizeMake(actualWidth, 1024) options:NSStringDrawingUsesLineFragmentOrigin context:nil]; // is the height to big? if (boundingRect.size.height > self.maxTextViewSize.height) { // reduce the point size for next iteration of loop pointSize -= decrementor; } // passes height test else { isValidPointSize = YES; } } } while (!isValidPointSize); return pointSize; 

Again, the above pseudo-code is based on my implementation (not intended to just replace what you have in return). Hope this helps!

+5
source share

try it

 UITextView *textViewObj;//initialise textview. textViewObj.autoresizesSubviews = NO; textViewObj.autoresizingMask = UIViewAutoresizingNone; 
0
source share

It really works in quick, get the original height of the text. Try it let

 size = cellQueue.contentLbl.sizeThatFits(CGSizeMake(cellQueue.contentLbl.frame.size.width,CGFloat(MAXFLOAT))) cellQueue.heightConstraintContentLbl.constant = size.height 
0
source share

All Articles