UITextView NSAttributedString Размер

Я работаю над приложением, которое использует UITextView.

UITextView должен увеличиваться или уменьшаться, чтобы соответствовать его тексту, как вертикально, так и горизонтально. Для этого я переопределяю sizeToFit в подклассе и устанавливаю границы следующим образом:

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

Проблема в том, что этот размер просто не отражает правильный размер строки, поскольку UITextView обрезает текст. Я устанавливаю краевые вставки на ноль, так что это не должно быть проблемой, верно?

На данный момент, я думаю, что это ошибка со свойством размера NSAttributedString, но то же самое происходит, если я использую boundingRectWithSize:options:context:

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

Поэтому, возможно, какой бы код ни выполнял вычисления макета для NSAttributedString, он не очень хорошо сочетается с вычислениями макета UITextView.

Вот пример проекта, который демонстрирует проблему.

Любые идеи приветствуются!

РЕДАКТИРОВАТЬ: Я также должен отметить, что следующее не работает либо:

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

3 ответа

Решение

Хотя и не идеально, я в конечном итоге использовал:

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

Я думаю, что вы правы - я не мог получить -[NSAttributedString boundingRectWithSize:options:context: ] вернуть значение, которое работало правильно для всех размеров шрифта...

Тем не менее я получил UILabel для правильного размера и нарисовал приписанные строки:

  • измените свой класс текстового представления на UILabel
  • использование -setAttributedText: на этикетке
  • задавать label.numberOfLines = 0 (Многострочный)
  • в -layoutSubviews вызова superview вашей метки -[ label sizeThatFits: ], чтобы получить правильный размер для метки....

Работал на меня...

РЕДАКТИРОВАТЬ: Вот мой файл ViewController.m:

@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

Решение Swift 3 для решения Tom Irvings выше:

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

    }
}
Другие вопросы по тегам