Как мне определить размер UITextView для его содержимого? - PullRequest
503 голосов
/ 08 сентября 2008

Есть ли хороший способ настроить размер UITextView в соответствии с его содержимым? Скажем, например, у меня есть UITextView, который содержит одну строку текста:

"Hello world"

Затем я добавляю еще одну строку текста:

"Goodbye world"

Есть ли хороший способ в Cocoa Touch получить rect, который будет содержать все строки в текстовом представлении, чтобы я мог соответствующим образом настроить родительский вид?

В качестве другого примера посмотрите на поле заметок для событий в приложении «Календарь» - обратите внимание, как ячейка (и UITextView, который она содержит) расширяется, чтобы содержать все строки текста в строке заметок.

Ответы [ 36 ]

583 голосов
/ 21 сентября 2013

Это работает как для iOS 6.1, так и для iOS 7:

- (void)textViewDidChange:(UITextView *)textView
{
    CGFloat fixedWidth = textView.frame.size.width;
    CGSize newSize = [textView sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)];
    CGRect newFrame = textView.frame;
    newFrame.size = CGSizeMake(fmaxf(newSize.width, fixedWidth), newSize.height);
    textView.frame = newFrame;
}

Или в Swift (Работает с Swift 4.1 в iOS 11)

let fixedWidth = textView.frame.size.width
let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
textView.frame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)

Если вам нужна поддержка iOS 6.1, вам также следует:

textview.scrollEnabled = NO;
575 голосов
/ 21 марта 2010

Это больше не работает на iOS 7 или выше

На самом деле существует очень простой способ изменения размера UITextView до правильной высоты содержимого. Это можно сделать с помощью UITextView contentSize.

CGRect frame = _textView.frame;
frame.size.height = _textView.contentSize.height;
_textView.frame = frame;

Следует отметить, что правильный contentSize доступен только после того, как UITextView был добавлен в представление с addSubview. До этого он равен frame.size

Это не будет работать, если включена автоматическая разметка. При автоматической компоновке общий подход заключается в использовании метода sizeThatFits и обновлении значения constant для ограничения высоты.

CGSize sizeThatShouldFitTheContent = [_textView sizeThatFits:_textView.frame.size];
heightConstraint.constant = sizeThatShouldFitTheContent.height;

heightConstraint - это ограничение макета, которое обычно устанавливается с помощью IBOutlet путем связывания свойства с ограничением высоты, созданным в раскадровке.


Просто добавлю к этому удивительному ответу 2014, если вы:

[self.textView sizeToFit];

есть разница в поведении только с iPhone6 ​​+ :

enter image description here

Только с 6+ (не 5 или 6) он добавляет «еще одну пустую строку» в UITextView. «Решение RL» прекрасно это исправляет:

CGRect _f = self.mainPostText.frame;
_f.size.height = self.mainPostText.contentSize.height;
self.mainPostText.frame = _f;

Исправляет проблему с «дополнительной линией» на 6+.

88 голосов
/ 28 октября 2014

Для создания динамического размера UITextView внутри UITableViewCell я обнаружил, что следующая комбинация работает в Xcode 6 с iOS 8 SDK:

  • Добавьте UITextView к UITableViewCell и ограничьте его в стороны
  • Установите для свойства UITextView s scrollEnabled значение NO. При включенной прокрутке кадр UITextView не зависит от размера его содержимого, но при отключенной прокрутке между ними есть взаимосвязь.
  • Если ваша таблица использует исходную высоту строки по умолчанию, равную 44, тогда она автоматически вычислит высоту строки, но если вы изменили высоту строки по умолчанию на другую, вам может потребоваться вручную включить автоматический расчет строки высоты в viewDidLoad:

    tableView.estimatedRowHeight = 150;
    tableView.rowHeight = UITableViewAutomaticDimension;
    

Для динамического определения размера UITextView s только для чтения, вот и все. Если вы разрешаете пользователям редактировать текст в UITextView, вам также необходимо:

  • Реализуйте метод textViewDidChange: протокола UITextViewDelegate и скажите tableView перекрашиваться каждый раз, когда текст редактируется:

    - (void)textViewDidChange:(UITextView *)textView;
    {
        [tableView beginUpdates];
        [tableView endUpdates];
    }
    
  • И не забудьте установить делегат UITextView где-нибудь, либо в Storyboard, либо в tableView:cellForRowAtIndexPath:

58 голосов
/ 01 октября 2015

Swift:

textView.sizeToFit()
56 голосов
/ 17 июля 2016

Очень простое в использовании решение с использованием кода и раскадровки.

По коду

textView.scrollEnabled = false

По раскадровке

Снимите флажок Включение прокрутки

enter image description here

Не нужно ничего делать кроме этого.

24 голосов
/ 08 февраля 2009

По моему (ограниченному) опыту

- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode

не учитывает символы новой строки, поэтому вы можете получить намного меньше CGSize, чем требуется на самом деле.

- (CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size

похоже, уважает переводы строк.

Кроме того, текст на самом деле не отображается в верхней части UITextView. В моем коде я установил новую высоту UITextView на 24 пикселя больше, чем высота, возвращаемая методами sizeOfFont.

22 голосов
/ 17 сентября 2013

В iOS6 вы можете проверить свойство contentSize UITextView сразу после установки текста. В iOS7 это больше не будет работать. Если вы хотите восстановить это поведение для iOS7, поместите следующий код в подкласс UITextView.

- (void)setText:(NSString *)text
{
    [super setText:text];

    if (NSFoundationVersionNumber > NSFoundationVersionNumber_iOS_6_1) {
        CGRect rect = [self.textContainer.layoutManager usedRectForTextContainer:self.textContainer];
        UIEdgeInsets inset = self.textContainerInset;
        self.contentSize = UIEdgeInsetsInsetRect(rect, inset).size;
    }
}
18 голосов
/ 25 июля 2014

Я опубликую правильное решение в нижней части страницы на случай, если кто-то проявит смелость (или достаточно отчаялся), чтобы прочитать этот вопрос.

Вот репозиторий gitHub для тех, кто не хочет читать весь этот текст: resizableTextView

Это работает с iOs7 (и я верю, что это будет работать с iOs8) и с автопоставкой. Вам не нужны магические числа, отключите разметку и все в таком духе. Короткое и элегантное решение.

Я думаю, что весь код, связанный с ограничениями, должен идти в метод updateConstraints. Итак, давайте сделаем наш собственный ResizableTextView.

Первая проблема, с которой мы здесь сталкиваемся, заключается в том, что мы не знаем реального размера контента до viewDidLoad метода. Мы можем взять длинный и ошибочный путь и рассчитать его на основе размера шрифта, разрывов строк и т. Д. Но нам нужно надежное решение, поэтому мы сделаем:

CGSize contentSize = [self sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)];

Итак, теперь мы знаем реальный размер контента независимо от того, где мы находимся: до или после viewDidLoad. Теперь добавьте ограничение высоты для textView (через раскадровку или код, независимо от того, как). Мы скорректируем это значение с помощью contentSize.height:

[self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
    if (constraint.firstAttribute == NSLayoutAttributeHeight) {
        constraint.constant = contentSize.height;
        *stop = YES;
    }
}];

Последнее, что нужно сделать, это сообщить суперклассу updateConstraints.

[super updateConstraints];

Теперь наш класс выглядит так:

ResizableTextView.m

- (void) updateConstraints {
    CGSize contentSize = [self sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)];

    [self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
        if (constraint.firstAttribute == NSLayoutAttributeHeight) {
            constraint.constant = contentSize.height;
            *stop = YES;
        }
    }];

    [super updateConstraints];
}

Красиво и чисто, верно? И вам не нужно иметь дело с этим кодом в ваших контроллерах !

Но подождите! Y НЕТ АНИМАЦИИ!

Вы можете легко анимировать изменения, чтобы textView плавно растягивался. Вот пример:

    [self.view layoutIfNeeded];
    // do your own text change here.
    self.infoTextView.text = [NSString stringWithFormat:@"%@, %@", self.infoTextView.text, self.infoTextView.text];
    [self.infoTextView setNeedsUpdateConstraints];
    [self.infoTextView updateConstraintsIfNeeded];
    [UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
        [self.view layoutIfNeeded];
    } completion:nil];
12 голосов
/ 09 сентября 2008

Вы пробовали [textView sizeThatFits:textView.bounds]?

Редактировать: sizeThatFits возвращает размер, но фактически не изменяет размер компонента. Я не уверен, что это то, что вы хотите, или [textView sizeToFit] больше, чем вы искали. В любом случае, я не знаю, будет ли он идеально соответствовать контенту, который вы хотите, но это первое, что нужно попробовать.

9 голосов
/ 20 сентября 2008

Другой метод - это поиск размера, который займет конкретная строка с использованием метода NSString:

-(CGSize)sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size

Возвращает размер прямоугольника, который соответствует заданной строке с заданным шрифтом. Передайте размер с желаемой шириной и максимальной высотой, а затем вы можете посмотреть на высоту, возвращаемую в соответствии с текстом. Существует версия, которая позволяет также указать режим разрыва строки.

Затем вы можете использовать возвращенный размер, чтобы изменить размер вашего представления.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...