I'm working on an app which uses a UITextView.
The UITextView should grow or shrink to fit its text, both vertically and horizontally. To do this, I'm overriding sizeToFit in a subclass, and I set the bounds like so:
- (void)sizeToFit {
[self setBounds:(CGRect){.size = self.attributedText.size}];
}
The problem is that this size just doesn't reflect the correct size of the string, as the UITextView clips the text. I'm setting the edge insets to zero, so that shouldn't be an issue right?
At this point, I think it's a bug with NSAttributedString's 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 maybe whatever code is doing layout calculations for NSAttributedString doesn't play nicely with UITextView's layout calculations.
Here is example project which demonstrates the issue.
Any ideas are welcome!
EDIT: I should also point out that the following doesn't work either:
- (void)sizeToFit {
[self setBounds:(CGRect){.size = [self sizeThatFits:self.attributedText.size]}];
}
Though not perfect, 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;
}
I think you're right--I couldn't get -[NSAttributedString boundingRectWithSize:options:context: ]
to return a value that worked correctly for all font sizes...
I did get a UILabel to correctly size and draw attributed strings however:
UILabel
-setAttributedText:
on the labellabel.numberOfLines = 0
(multiline)-layoutSubviews
of your label's superview call -[ label sizeThatFits: ] to get the correct size for the label....Worked for me...
EDIT: Here's 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With