UITextView NSAttributedString Size

I am working on an application that uses a UITextView.

A UITextView needs to grow or shrink to fit its text, both vertically and horizontally. To do this, I override sizeToFit in the subclass, and I set the bounds as follows:

- (void)sizeToFit { [self setBounds:(CGRect){.size = self.attributedText.size}]; } 

The problem is that this size simply does not reflect the correct row size, since the UITextView captures the text. I set cross-inserts to zero, so this should not be a problem?

At this point, I think this is an error using the NSAttributedString size property, but the same thing happens if I use boundingRectWithSize: options: context:

 [self setBounds:(CGRect){.size = [self.attributedText boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:0 context:nil].size}]; 

So it’s possible that no matter what code performs layout calculations for NSAttributedString, it doesn’t play well with UITextView layout calculations.

Here is an example project that demonstrates the problem .

Any ideas are welcome!

EDIT: I should also indicate that the following does not work:

 - (void)sizeToFit { [self setBounds:(CGRect){.size = [self sizeThatFits:self.attributedText.size]}]; } 
+8
ios objective-c uikit uitextview nsattributedstring
source share
3 answers

Although this is not ideal, I ended up using:

 - (void)sizeToFit { CGSize textSize = self.attributedText.size; CGSize viewSize = CGSizeMake(textSize.width + self.firstCharacterOrigin.x * 2, textSize.height + self.firstCharacterOrigin.y * 2); [self setBounds:(CGRect){.size = [self sizeThatFits:viewSize]}]; } - (CGPoint)firstCharacterOrigin { if (!self.text.length) return CGPointZero; UITextRange * range = [self textRangeFromPosition:[self positionFromPosition:self.beginningOfDocument offset:0] toPosition:[self positionFromPosition:self.beginningOfDocument offset:1]]; return [self firstRectForRange:range].origin; } 
+3
source share

I think you're right - I could not get -[NSAttributedString boundingRectWithSize:options:context: ] to return a value that worked correctly for all font sizes ...

I got a UILabel for the correct size and drawing of attributed strings:

  • change the text view class to UILabel
  • use -setAttributedText: on the shortcut
  • set label.numberOfLines = 0 (multi-line)
  • in -layoutSubviews your label supervisor call is [label sizeThatFits:] to get the correct size for the label ....

Worked for me ...

EDIT: Here is my ViewController.m file:

 @interface View : UIView @property ( nonatomic, strong ) UITextView * textView ; @property ( nonatomic, strong ) UISlider * fontSizeSlider ; @end @implementation View -(void)layoutSubviews { CGRect bounds = self.bounds ; self.textView.layer.anchorPoint = (CGPoint){ 0.0f, 0.5f } ; self.textView.layer.position = (CGPoint){ CGRectGetMinX( bounds ), CGRectGetMidY( bounds ) } ; self.textView.bounds = (CGRect){ .size = [ self.textView sizeThatFits:bounds.size ] } ; self.fontSizeSlider.frame = CGRectMake(5, CGRectGetMaxY(bounds) - 30, bounds.size.width - 10, 25) ; } @end @interface ViewController () @end @implementation ViewController -(void)loadView { self.view = [[ View alloc ] initWithFrame:CGRectZero ] ; } - (void)viewDidLoad { [super viewDidLoad]; NSMutableAttributedString * aString = [[ NSMutableAttributedString alloc ] initWithString:@"Some test text in a scaling text view" ] ; NSDictionary * attributes = @{ NSForegroundColorAttributeName : [ UIColor redColor ] } ; [ aString setAttributes:attributes range:(NSRange){ 5, 4 } ] ; textView = [[UITextView alloc] initWithFrame:CGRectZero]; [textView setAttributedText:aString ]; [textView setFont:[UIFont systemFontOfSize:18]]; [textView setCenter:CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds))]; ((View*)self.view).textView = textView ; [self.view addSubview:textView]; [textView release]; UISlider * fontSizeSlider = [[UISlider alloc] initWithFrame:CGRectMake(5, CGRectGetMaxY(self.view.bounds) - 30, self.view.bounds.size.width - 10, 25)]; [fontSizeSlider addTarget:self action:@selector(fontSizeSliderDidChange:) forControlEvents:UIControlEventValueChanged]; [fontSizeSlider setMinimumValue:5]; [fontSizeSlider setMaximumValue:100]; [fontSizeSlider setValue:textView.font.pointSize]; ((View*)self.view).fontSizeSlider = fontSizeSlider ; [self.view addSubview:fontSizeSlider]; [fontSizeSlider release]; } - (void)fontSizeSliderDidChange:(UISlider *)sender { [ textView setFont:[textView.font fontWithSize:sender.value]]; [ self.view setNeedsLayout ] ; } @end 
0
source share

quick solution 3 to solve the exact workaround above:

 extension UITextView { func sizeToFitAttributedString() { let textSize = self.attributedText.size() let viewSize = CGSize(width: textSize.width + self.firstCharacterOrigin.x * 2, height: textSize.height + self.firstCharacterOrigin.y * 2) self.bounds.size = self.sizeThatFits(viewSize) } private var firstCharacterOrigin: CGPoint { if self.text.lengthOfBytes(using: .utf8) == 0 { return .zero } let range = self.textRange(from: self.position(from: self.beginningOfDocument, offset: 0)!, to: self.position(from: self.beginningOfDocument, offset: 1)!) return self.firstRect(for: range!).origin } } 
0
source share

All Articles