NSAttributedString reports wrong sizes for UITextView sizeThatFits and boundingRectWithSize with the right settings

I have an NSAttributedString that reports a RectWithSize restriction (and by extension UITextView that incorrectly calculates the size of sizeThatFits) when the font size decreases from the size of the font used to create it.

This does not happen on all NSAttributedStrings for which I perform similar operations, so here are the steps to play.

  • Use a custom font that does not include the full character set in Unicode.
  • Make sure the string contains characters in this “unsupported” set. iOS will display them as Helvetica in the correct size.
  • Decrease the font on all font attributes in NSAttributedString. My code for this that created the problem is as follows.

Inside a subclass of UITextView:

NSMutableAttributedString *mutableString = [self.attributedText mutableCopy]; [mutableString enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, mutableString.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) { if (value) { UIFont *oldFont = (UIFont *)value; UIFont *newFont = [oldFont fontWithSize:oldFont.pointSize - 1]; [mutableString removeAttribute:NSFontAttributeName range:range]; [mutableString addAttribute:NSFontAttributeName value:newFont range:range]; } }]; self.attributedText = [mutableString copy]; 

I noticed that when I run this code in a while that checks sizeThatFits to know when the text is small enough to match that in some cases I will race to zero. The height is calculated as 60px for any font value less than I started with 50px.

When the NSLog in NSAttributedString I found that there are several attributes that I did not add with the NSOriginalFont key, which is not on the list of supported attributes here . What happens to NSOriginalFont? Why is my size not calculated correctly?

+7
ios objective-c fonts ios7 textkit
source share
4 answers

I ended up fixing it, but found a lack of information on the Internet about it, so I decided to write my decision here.

NSOriginalFont attributes are created when the font used does not support one or more characters per line. NSAttributedString adds these attributes that track that the font was “presumably” before the Helvetica replacement took place. I could make a situation where this is useful (the all-caps font that you sometimes use uppercaseString: on?), But this was not useful to me.

In fact, it was harmful. As I repeated my font-related attributes to reduce the size as shown above, the visible size of the text was reduced, but the NSOriginalFont attribute kept the link to a large size.

There is no built-in constant for NSOriginalFont, but if you call it by name, it can be separated from NSMutableAttributedString. If you do this, you will get the correct results from sizeThatFits, boundingRectWithSize and other similar functions, assuming that you are passing the correct parameters.

I ended up creating a simple category method in NSMutableAttributedString, which is shown below, which works well.

NSMutableAttributedString + StripOriginalFont.h

 @interface NSMutableAttributedString (StripOriginalFont) - (void) stripOriginalFont; @end 

NSMutableAttributedString + StripOriginalFont.m

 @implementation NSMutableAttributedString (StripOriginalFont) - (void) stripOriginalFont{ [self enumerateAttribute:@"NSOriginalFont" inRange:NSMakeRange(0, self.length) options:0 usingBlock:^(id value, NSRange range, BOOL *stop) { if (value){ [self removeAttribute:@"NSOriginalFont" range:range]; } }]; } @end 

Presumably, you can just change it to keep it in-sync, and not delete it completely, but it wasn’t useful to me for this particular project.

+12
source share

Create an NSTextStorage object and run with the attributering attribute. and calculate the boundaries.

 NSTextStorage *attributedText = [[NSTextStorage alloc] initWithAttributedString:[[NSAttributedString alloc] initWithString:text attributes:@{NSFontAttributeName:systemFont}]]; CGRect textRect = [attributedText boundingRectWithSize:CGSizeMake(textW, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin context:nil]; 
+1
source share

I don’t know if this will help solve your problem, but look at my solution for automatically selecting a UITextView text here: https://stackoverflow.com/a/167268/

0
source share

I had the same problem. when I call textView.setAttributedString() , it will automatically add the NSOriginalFont attribute for me, which will result in the wrong size. I also use sizeThatFits to calculate height.

The reason we got the wrong size is because of the textView using NSOriginalFont to calculate the size, which is not suitable for the modified NSFont .

But if we use NSTextStorage to create the strring attribute and call textView.setAttributedText , then it will not add NSOriginalFont (I don't know why, but this fixes my problem), and calculating the size will get the correct answer.

Simple code:

 func getAttributedStringForTextView(content: String) -> NSAttributedString { var attriString = NSMutableAttributedString(string: content) // add attributes here ... // at last, use an NSTextStorage to wrap the result return NSTextStorage(attributedString: attriString) } 

Hope this helps.

0
source share

All Articles