UITextView NSAttributedString Size

泪湿孤枕 提交于 2019-12-12 07:49:01

问题


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]}];
}

回答1:


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;
}



回答2:


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:

  • change your text view class to UILabel
  • use -setAttributedText: on the label
  • set label.numberOfLines = 0 (multiline)
  • in -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



回答3:


swift 3 solution for tom irvings solution 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

    }
}


来源:https://stackoverflow.com/questions/14545096/uitextview-nsattributedstring-size

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!