Как изменить размер UITextView в соответствии с его содержимым?

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

"Hello world"

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

"Goodbye world"

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

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


person drewh    schedule 08.09.2008    source источник
comment
Пожалуйста, подумайте об обновлении правильного ответа, так как принятый приводит к ненужным вычислениям, теперь для этой проблемы есть свойство contentSize.   -  person Juan Boero    schedule 18.12.2015
comment
почему нет вопросов, как это сделать нединамически? Я даже не могу понять, как это сделать статично. И предоставленные решения, похоже, не отменяют ограничения в раскадровке.   -  person pete    schedule 14.08.2020


Ответы (41)


Это работает как для 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;
person jhibberd    schedule 21.09.2013
comment
Протестировано на iOS 7 для изменения размера UITextView в UITableViewCell. Отлично работает. Это единственный ответ, который, наконец, сработал для меня. Спасибо! - person Alex; 30.11.2013
comment
Мне нужен textview.scrollEnabled = NO;, чтобы текст не обрезался даже в iOS 7. - person Brian; 07.01.2014
comment
Я рекомендую использовать макрос CGFLOAT_MAX вместо MAXFLOAT. Правильно на обеих 32- и 64-битных платформах. - person eonil; 27.08.2014
comment
это сработало для меня, но затем, когда я пытаюсь прокрутить родительский вид до textView, когда пользователь переходит на его редактирование, происхождение отключено ... - person scientiffic; 07.09.2014
comment
Пришлось отключить автоматическое размещение для моего файла пера, чтобы это работало должным образом, иначе первый и второй символы новой строки искажали размер текстового представления. - person user3344977; 30.11.2014
comment
Также вы можете использовать INFINITY вместо CGFLOAT_MAX и MAXFLOAT. Выглядит смешнее - person fnc12; 27.01.2015
comment
Тот, кто сталкивается с небольшими колебаниями перед вводом в следующую строку, добавляет этот `NSRange bottom = NSMakeRange (textView.text.length -1, 1); [textView scrollRangeToVisible: внизу]; ` - person ajay_nasa; 28.08.2015
comment
Сработало отлично !! ПРИМЕЧАНИЕ для использования Autolayout: мне пришлось сначала установить верхнее / нижнее пространство для ограничений супервизора, чтобы это сработало. - person Fateh Khalsa; 14.09.2015
comment
Почему в версии Swift есть два отдельных вызова sizeThatFits (...)? Для чего первая строчка? - person nekonari; 05.05.2016
comment
Если у кого-то возникла проблема с изменением размера, поместите этот код выше, чтобы переопределить метод func layoutSubviews (). - person Daniel Kuta; 12.08.2016
comment
Это прекрасно работает. Был сбит с толку на несколько минут, потому что у меня все еще было установлено ограничение автопластинга высоты в IB для текстового представления, но после его удаления все в порядке. - person jbll; 06.10.2016
comment
в этом методе есть ошибка в ios 9. размер текстового представления достигает нормальной высоты при выборе текста из текстового представления - person Prashant Ghimire; 26.10.2016
comment
Просто не забудьте добавить это в viewDidLayoutSubviews, а не в viewWillAppear - person Derek; 23.01.2017
comment
Когда textview.scrollEnabled = NO; Мой курсор не виден, после ввода первого символа он становится видимым. Когда прокрутка включена, при вводе текста верхний текст прокручивается слишком сильно (чрезмерная прокрутка), после ввода первого символа прокрутка исправляется сама. Поэтому оставление прокрутки включенной и использование этой строки исправляет это для меня: NSRange bottom = NSMakeRange (textView.text.length -1, 1); [textView scrollRangeToVisible: внизу]; Как написал aiay_nasa. - person Wayne; 04.09.2017
comment
Это отлично работает, просто не забудьте убедиться, что у вас не установлены ограничения по высоте для TextView в построителе интерфейса. - person velval; 30.05.2018
comment
В отладчике пользовательского интерфейса размер выглядит отлично, но текст не переносится - я поместил это в initWithFrame, может ли это быть проблемой? - person ArielSD; 03.05.2019
comment
В качестве альтернативы установите ограничение высоты textView равным расчетной высоте. - person JLundell; 17.08.2019
comment
Что касается предложения @ DanielKuta, обратите внимание, что layoutSubviews () - это функция UIView, отлично подходит, если вы подклассифицируете текстовое представление или родительский объект. Если вы делаете это в контроллере представления, вам нужно viewWillLayoutSubviews (). - person JLundell; 17.08.2019
comment
для дальнейшего использования. Если вы используете Autolayout, проверьте ответ Alok, это все, что вам нужно, так просто. - person PakitoV; 11.06.2020
comment
Я не думаю, что это может преодолеть ограничения. Как указать это изначально в ограничении раскадровки? У меня даже нет динамического текста; просто статично. - person pete; 14.08.2020

Это больше не работает на 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 ​​+:

введите описание изображения здесь

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

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

Исправлена ​​проблема с "лишней строкой" на 6+.

person Ronnie Liew    schedule 21.03.2010
comment
Ты спас мне день. Я боролся с расширением функции класса selfCalculating, забывая об этом. Спасибо. - person Lukasz; 05.01.2011
comment
@Ronnie, спасибо за ваш код, я использую ваш код, но когда мое содержимое textview достигает строки 2000, этот код замедляет работу моего приложения, вы можете сказать мне, почему? - person R. Dewi; 28.01.2011
comment
@Ronnie, где мы должны писать этот код. Я хотел бы сначала отобразить одну строку кадра, и по мере того, как типы пользователей в кадре должны увеличиваться, чтобы разместить текст без прокрутки вверх. - person Yogesh; 28.04.2011
comment
Любопытнее: когда я уменьшаю текст, так что contentSize имеет высоту всего 79, добавляя 100, я получаю только 1 строку. Мне нужно добавить 200 - установить высоту UITextView на 279 - чтобы отобразить 3 строки. - person Hot Licks; 23.06.2011
comment
Ага! Я устанавливал высоту текстового представления, а затем настраивал высоту содержащего представления. Видимо, когда я это делал, высота текстового представления регулировалась. Установка высоты содержащего представления сначала заставляет все работать так, как ожидалось (или, что лучше, на что надеялись). - person Hot Licks; 23.06.2011
comment
Использование [_textView sizeToFit] обновляет свойство contentSize, удаляя требование заранее добавлять его в scrollView. - person Rodrigo; 22.11.2011
comment
Вы также можете добавить frame.size.height + = (_textView.contentInset.top + _textView.contentInset.bottom); Если вы изменили contentInset, чтобы не оставлять пустую рамку вокруг текста. - person Maciej Swic; 27.12.2012
comment
_textView.scrollEnabled = НЕТ; тоже нужен. В противном случае я заканчивал смещенным содержимым после новой строки. - person Bo.; 15.03.2013
comment
Теперь, когда вы это сказали, это так очевидно. Я пытался вычислить высоту с помощью sizeWithFont:forWidth:lineBreakMode, спасибо! - person Adam Carter; 26.03.2013
comment
Это отличное решение, но оно не работает при использовании автоматического раскладки. Есть ли у вас какие-либо идеи, как добиться этого с включенным автоопределением? - person Xander Dunn; 15.04.2013
comment
При использовании автоматического раскладки вызовите layoutIfNeeded в супервизоре. Тогда вы можете взять высоту от contentSize. - person phatmann; 21.05.2013
comment
Если вы заставили его работать на iOS 7, помогите в этом вопросе: stackoverflow.com/questions/19049532/ - person Henrik Erlandsson; 27.09.2013
comment
Однако речь идет об изменении размера внешнего контейнера. Это поведение изменилось в iOS 7. В связанном вопросе я показываю, где добавить sizeToFit и layoutIfNeeded. - person Henrik Erlandsson; 21.10.2013
comment
@HenrikErlandsson, ваш комментарий сэкономил мне сейчас много работы :) - person giorashc; 17.11.2013
comment
У меня это не работает в 2016 году. Возможно, устарело. Высота содержимого всегда равна высоте кадра, даже когда я добавил addSubView. - person zztop; 20.05.2016

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

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

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

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

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

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

person Brett Donald    schedule 28.10.2014
comment
Лучший ответ для меня. Работал отлично, и UITableViewAutomaticDimension был добавлен в iOS 5, поэтому он обратно совместим. - person smmelzer; 09.04.2015
comment
У меня не совсем сработало. Метод textViewDidChange заставляет мое представление таблицы подпрыгивать каждый раз, когда я набираю символ. Я предлагаю вместо использования этого метода в методе tableView: cellForRowAtIndexPath для строки с текстовым представлением на ней, если вы находитесь в режиме только для чтения, для параметра .scrollEnabled установлено значение NO, и если вы находитесь в режиме редактирования, установленном прокрутите до ДА. Таким образом, при простом отображении он всегда будет отображать полный текст, а при редактировании он будет начинаться с полного текста, и при добавлении следующего текста он останется того же размера, а предыдущий текст будет прокручиваться сверху - person SimonTheDiver; 18.12.2015
comment
кроме того, я добавил ограничение ›= height к текстовому представлению, поскольку пустое текстовое поле не имело высоты и его нельзя было нажать, чтобы начать редактирование. - person SimonTheDiver; 18.12.2015
comment
Отличное решение. Для меня tableView.rowHeight = UITableViewAutomaticDimension был не нужен. - person Christopher Pickslay; 21.01.2016
comment
Еще раз спасибо, Бретт Дональд! Я забыл, как это сделать, и через год снова нашел ваш ответ ????. - person Eric Conner; 01.12.2016
comment
Для всех, кто столкнется с этим ответом в будущем, главное, что вам нужно знать, это просто UITextView.scrollEnabled = @NO - person Brett Donald; 05.01.2017
comment
Это заставляет экран подпрыгивать каждый раз при обновлении, как сказал @SimonTheDiver - person Stefanija; 22.02.2018

Очень простое рабочее решение, использующее как код, так и раскадровку.

По коду

textView.scrollEnabled = false

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

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

введите описание изображения здесь

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

person Alok    schedule 17.07.2016
comment
Это правильный ответ. В StoryBoard у вас есть TextView AutoSize, как и в UILabel. Все, что нам нужно сделать, это установить scrollEnabled = false. - person pnavk; 13.08.2016
comment
По сути, это правильно: если вы используете ограничения Autolayout и отключаете прокрутку, все работает. - person Dan Rosenstark; 10.04.2017
comment
Большое спасибо. Это было так просто, просто отключите прокрутку в раскадровке или коде, это будет как UILabel со строками 0. - person samir105; 10.03.2018
comment
@Mansuu .... Это не гарантированное решение, так как есть вещи, которые вы можете сделать, чтобы переопределить внутренний размер содержимого, и если вы измените текст после размещения представления, он не будет обновляться, если вы не установите это тоже вверх. Но эти вещи остаются верными и для установки numberOfLines = 0 в текстовом поле. Если это не сработает, возможно, у вас есть другие ограничения, которые неявно устанавливают высоту. - person Jake T.; 05.07.2018
comment
Ваш ответ приемлем, но если мы добавим ограничение по высоте, это не сработает. - person iOS; 28.08.2018
comment
@iOS, если вы добавляете ограничение по высоте, сделайте приоритет ограничений ниже, иначе это не сработает. - person Alok; 28.08.2018
comment
Объяснение, почему это работает: stackoverflow.com/a/65861960/815742 - person fumoboy007; 23.01.2021

Swift:

textView.sizeToFit()
person Juan Boero    schedule 01.10.2015
comment
Кроме того, я обнаружил, что мне нужно установить для параметра scrolling enabled значение false, чтобы это работало. textView.scrollEnabled = false - person tmarkiewicz; 19.03.2016
comment
Не забудьте вызвать textView.layoutIfNeeded () после textView.sizeToFit () - person Daniel Kuta; 01.06.2016
comment
@tmarkiewicz Это работает, когда текст умещается на экране вашего устройства, но это будет проблемой, когда ваш текст больше, чем место на экране. Вы не сможете прокрутить его, чтобы увидеть остальной текст. - person Francisco Romero; 03.10.2016
comment
Это ответ !!! Нет необходимости в более сложных решениях !!! @FranciscoRomero Почему бы не поместить текстовое представление в ячейку таблицы или ячейку представления коллекций, тогда вы всегда можете прокрутить таблицу или представление коллекций .... - person Ben Smith; 05.02.2019

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

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

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

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

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

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

person user63934    schedule 08.02.2009
comment
Он получает размер, как если бы он рисовал текст на UILabel. Если вы установите этот размер для фрейма uitextview, тогда у вас все равно будет необходимая прокрутка. - person Kevlar; 04.12.2009

В 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;
    }
}
person phatmann    schedule 16.09.2013
comment
Где я могу прочитать об этом подробнее? Я совершенно не понимаю, как выглядит новое поведение в iOS 7 по этому поводу. - person Stian Høiland; 19.09.2013
comment
Что означает переменная CGSize size? - person Tchelow; 02.11.2013
comment
Я снял размер, его там не должно было быть. - person phatmann; 26.11.2013

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

Вот репозиторий 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];
}

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

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

Вы можете легко анимировать изменения, чтобы 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];
person Nikita Took    schedule 25.07.2014
comment
Используйте CGFLOAT_MAX, а не FLT_MAX. - person rob mayoff; 23.09.2014
comment
Хотел бы я дать вам больше одного голоса за это потрясающее решение !! - person Scooter; 03.10.2014

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

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

person Mike McMaster    schedule 08.09.2008
comment
sizetofit спасает положение - person michaelsnowden; 24.06.2014

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

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

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

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

person Code Addict    schedule 20.09.2008

Мы можем сделать это с помощью ограничений.

  1. Установите ограничения высоты для UITextView. введите описание изображения здесь

2. Создайте IBOutlet для этого ограничения высоты.

 @property (weak, nonatomic) IBOutlet NSLayoutConstraint *txtheightconstraints;

3. не забудьте установить делегата для вашего текстового просмотра.

4.

-(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);
    NSLog(@"this is updating height%@",NSStringFromCGSize(newFrame.size));
    [UIView animateWithDuration:0.2 animations:^{
                  _txtheightconstraints.constant=newFrame.size.height;
    }];

}

затем обновите свое ограничение следующим образом :)

person Kishore Kumar    schedule 04.03.2016
comment
это ФАНТАСТИЧЕСКИЙ ник! - person Fattie; 19.02.2017
comment
За установкой константы должен следовать [textView layoutIfNeeded]; в блоке анимации. (Анимация автоматического раскладки делает это именно так.) - person Will Larche; 05.04.2017

Если у вас нет UITextView под рукой (например, вы определяете размер ячеек табличного представления), вам придется рассчитать размер, измерив строку, а затем учитывая 8 pt заполнения с каждой стороны UITextView. Например, если вы знаете желаемую ширину текстового представления и хотите определить соответствующую высоту:

NSString * string = ...;
CGFloat textViewWidth = ...;
UIFont * font = ...;

CGSize size = CGSizeMake(textViewWidth - 8 - 8, 100000);
size.height = [string sizeWithFont:font constrainedToSize:size].height + 8 + 8;

Здесь каждые 8 ​​соответствуют одному из четырех ребер с заполнением, а 100000 - это просто очень большой максимальный размер.

На практике вы можете добавить дополнительные font.leading к высоте; это добавляет пустую строку под вашим текстом, что может выглядеть лучше, если есть визуально тяжелые элементы управления непосредственно под текстовым представлением.

person Becca Royal-Gordon    schedule 24.10.2012
comment
отличный результат в iOS 8.1 - person Logic; 31.03.2015
comment
Прокладка была для меня ключом - person thibaut noah; 10.12.2016

Начиная с iOS 8, можно использовать функции автоматического макета UITableView для автоматического изменения размера UITextView без какого-либо специального кода. Я поместил в github проект, который демонстрирует это в действии, но вот ключ:

  1. В UITextView должна быть отключена прокрутка, что можно сделать программно или через построитель интерфейса. Если прокрутка включена, размер не изменится, потому что прокрутка позволяет просматривать более крупный контент.
  2. В viewDidLoad для UITableViewController необходимо установить значение для EstimatedRowHeight, а затем установить rowHeight на UITableViewAutomaticDimension.

- (void)viewDidLoad {
    [super viewDidLoad];
    self.tableView.estimatedRowHeight = self.tableView.rowHeight;
    self.tableView.rowHeight = UITableViewAutomaticDimension;
}
  1. Целевой объект развертывания проекта должен быть iOS 8 или выше.
person Chuck Krutsinger    schedule 04.06.2015
comment
Подобные ответы не приветствуются. Вы должны разместить здесь соответствующий код. - person Antzi; 30.06.2015

В сочетании с ответом Майка Макмастера вы можете сделать что-то вроде:

[myTextView setDelegate: self];

...

- (void)textViewDidChange:(UITextView *)textView {
  if (myTextView == textView) {
     // it changed.  Do resizing here.
  }
}
person Olie    schedule 06.11.2008

Я нашел способ изменить высоту текстового поля в соответствии с текстом внутри него, а также расположить метку под ним в зависимости от высоты текстового поля! Вот код.

UITextView *_textView = [[UITextView alloc] initWithFrame:CGRectMake(10, 10, 300, 10)];
NSString *str = @"This is a test text view to check the auto increment of height of a text view. This is only a test. The real data is something different.";
_textView.text = str;

[self.view addSubview:_textView];
CGRect frame = _textView.frame;
frame.size.height = _textView.contentSize.height;
_textView.frame = frame;

UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(10, 5 + frame.origin.y + frame.size.height, 300, 20)];
lbl.text = @"Hello!";
[self.view addSubview:lbl];
person dheeraj    schedule 14.03.2011
comment
Здесь '5' в координате Y UILabel - это промежуток, который я хочу между textView и Label. - person dheeraj; 14.03.2011

Ребята, использующие автопрокладку, и ваш sizetofit не работает, тогда проверьте ограничение ширины один раз. Если вы пропустили ограничение ширины, высота будет точной.

Нет необходимости использовать какой-либо другой API. всего одна строчка решила бы всю проблему.

[_textView sizeToFit];

Здесь меня интересовала только высота, сохраняя фиксированную ширину, и я пропустил ограничение ширины моего TextView в раскадровке.

И это должно было показать динамический контент из сервисов.

Надеюсь, это поможет ..

person Swaroop S    schedule 02.01.2015

Достаточно следующих вещей:

  1. Не забудьте установить для параметра прокрутка включена значение НЕТ для вашего UITextView:

введите описание изображения здесь

  1. Правильно установите ограничения автоматического макета.

Вы даже можете использовать UITableViewAutomaticDimension.

person Bartłomiej Semańczyk    schedule 02.11.2015

Я просмотрел все ответы, и все они сохраняют фиксированную ширину и регулируют только высоту. Если вы хотите отрегулировать также ширину, вы можете очень легко использовать этот метод:

поэтому при настройке текстового представления отключите прокрутку

textView.isScrollEnabled = false

а затем в методе делегата func textViewDidChange(_ textView: UITextView) добавьте этот код:

func textViewDidChange(_ textView: UITextView) {
    let newSize = textView.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude))
    textView.frame = CGRect(origin: textView.frame.origin, size: newSize)
}

Выходы:

введите описание изображения здесь

введите описание изображения здесь

person Peter Stajger    schedule 21.03.2017
comment
метод делегата не вызывается @Peter Stajger - person Mansuu....; 29.08.2017

отключить прокрутку

добавить константы

и добавьте свой текст

[yourTextView setText:@"your text"];
[yourTextView layoutIfNeeded];

если вы используете UIScrollView, вы тоже должны добавить это;

[yourScrollView layoutIfNeeded];

-(void)viewDidAppear:(BOOL)animated{
    CGRect contentRect = CGRectZero;

    for (UIView *view in self.yourScrollView.subviews) {
         contentRect = CGRectUnion(contentRect, view.frame);
    }
    self.yourScrollView.contentSize = contentRect.size;
}
person alicanozkara    schedule 10.01.2018

Использование UITextViewDelegate - самый простой способ:

func textViewDidChange(_ textView: UITextView) {
    textView.sizeToFit()
    textviewHeight.constant = textView.contentSize.height
}
person f0go    schedule 27.02.2020

Это отлично сработало, когда мне нужно было сделать текст в UITextView подходящим для определенной области:

// The text must already be added to the subview, or contentviewsize will be wrong.

- (void) reduceFontToFit: (UITextView *)tv {
    UIFont *font = tv.font;
    double pointSize = font.pointSize;

    while (tv.contentSize.height > tv.frame.size.height && pointSize > 7.0) {
        pointSize -= 1.0;
        UIFont *newFont = [UIFont fontWithName:font.fontName size:pointSize];
        tv.font = newFont;
    }
    if (pointSize != font.pointSize)
        NSLog(@"font down to %.1f from %.1f", pointSize, tv.font.pointSize);
}
person Bill Cheswick    schedule 08.10.2012

вот быстрая версия @jhibberd

    let cell:MsgTableViewCell! = self.tableView.dequeueReusableCellWithIdentifier("MsgTableViewCell", forIndexPath: indexPath) as? MsgTableViewCell
    cell.msgText.text = self.items[indexPath.row]
    var fixedWidth:CGFloat = cell.msgText.frame.size.width
    var size:CGSize = CGSize(width: fixedWidth,height: CGFloat.max)
    var newSize:CGSize = cell.msgText.sizeThatFits(size)
    var newFrame:CGRect = cell.msgText.frame;
    newFrame.size = CGSizeMake(CGFloat(fmaxf(Float(newSize.width), Float(fixedWidth))), newSize.height);
    cell.msgText.frame = newFrame
    cell.msgText.frame.size = newSize        
    return cell
person Fareed Alnamrouti    schedule 07.03.2015
comment
Вопрос действительно зависит от языка. Действительно ли необходимо возвращаться и писать быстрые порты для всех ответов на вопросы, связанные с UIKit? Похоже, это способствует шумному разговору. - person eremzeit; 05.04.2015
comment
проблема в том, что у большинства этих вопросов нет определенного языка программирования, поэтому вы найдете его в Google, даже если будете искать быстрый язык - person Fareed Alnamrouti; 05.04.2015

Для iOS 7.0 вместо установки frame.size.height на contentSize.height (который в настоящее время ничего не делает) используйте [textView sizeToFit].

См. этот вопрос.

person Stian Høiland    schedule 20.09.2013

Это прекрасно работает для Swift 5, если вы хотите подогнать текст под свой TextView, когда пользователь напишет текст на лету.

Просто реализуйте UITextViewDelegate с помощью:

func textViewDidChange(_ textView: UITextView) {
    let newSize = textView.sizeThatFits(CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude))
    textView.frame.size = CGSize(width: newSize.width, height: newSize.height)
}
person atereshkov    schedule 09.12.2019

если сюда попадет какой-либо другой, это решение сработает для меня, 1 "Ronnie Liew" +4 "user63934" (мой текст поступает из веб-службы): обратите внимание на 1000 (ничто не может быть таким большим "в моем случае")

UIFont *fontNormal = [UIFont fontWithName:FONTNAME size:FONTSIZE];

NSString *dealDescription = [client objectForKey:@"description"];

//4
CGSize textSize = [dealDescription sizeWithFont:fontNormal constrainedToSize:CGSizeMake(containerUIView.frame.size.width, 1000)];

CGRect dealDescRect = CGRectMake(10, 300, containerUIView.frame.size.width, textSize.height);

UITextView *dealDesc = [[[UITextView alloc] initWithFrame:dealDescRect] autorelease];

dealDesc.text = dealDescription;
//add the subview to the container
[containerUIView addSubview:dealDesc];

//1) after adding the view
CGRect frame = dealDesc.frame;
frame.size.height = dealDesc.contentSize.height;
dealDesc.frame = frame;

И это ... Ура

person Alejo JM    schedule 30.07.2012

Надеюсь это поможет:

- (void)textViewDidChange:(UITextView *)textView {
  CGSize textSize = textview.contentSize;
  if (textSize != textView.frame.size)
      textView.frame.size = textSize;
}
person Dr. Marty    schedule 17.03.2010
comment
Это не работает! Это вызывает ошибку: lvalue требуется как левый операнд присваивания - person leviathan; 07.06.2010
comment
Вы можете назначить только textView.frame. - person jweyrich; 01.03.2013

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

CGSize textViewSize = [YOURTEXTVIEW.text sizeWithFont:[UIFont fontWithName:@"SAMPLE_FONT" size:14.0]
                       constrainedToSize:CGSizeMake(YOURTEXTVIEW.frame.size.width, FLT_MAX)];

или вы можете ИСПОЛЬЗОВАТЬ

CGSize textViewSize = [YOURTEXTVIEW.text sizeWithFont:[UIFont fontWithName:@"SAMPLE_FONT" size:14.0]
                       constrainedToSize:CGSizeMake(YOURTEXTVIEW.frame.size.width, FLT_MAX) lineBreakMode:NSLineBreakByTruncatingTail];
person Manju    schedule 06.09.2013

Для тех, кто хочет, чтобы текстовое представление действительно двигалось вверх и сохраняло положение нижней строки

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

if(frame.size.height > textView.frame.size.height){
    CGFloat diff = frame.size.height - textView.frame.size.height;
    textView.frame = CGRectMake(0, textView.frame.origin.y - diff, textView.frame.size.width, frame.size.height);
}
else if(frame.size.height < textView.frame.size.height){
    CGFloat diff = textView.frame.size.height - frame.size.height;
    textView.frame = CGRectMake(0, textView.frame.origin.y + diff, textView.frame.size.width, frame.size.height);
}
person Gal Blank    schedule 30.11.2014

Единственный код, который будет работать, - это тот, который использует SizeToFit, как в ответе jhibberd выше, но на самом деле он не будет работать, если вы не вызовете его в ViewDidAppear или не подключите его к событию изменения текста UITextView.

person Mohammad Zekrallah    schedule 08.04.2015

Основываясь на ответе Никиты Тука, я пришел к следующему решению в Swift, которое работает на iOS 8 с автоматическим размещением:

    descriptionTxt.scrollEnabled = false
    descriptionTxt.text = yourText

    var contentSize = descriptionTxt.sizeThatFits(CGSizeMake(descriptionTxt.frame.size.width, CGFloat.max))
    for c in descriptionTxt.constraints() {
        if c.isKindOfClass(NSLayoutConstraint) {
            var constraint = c as! NSLayoutConstraint
            if constraint.firstAttribute == NSLayoutAttribute.Height {
                constraint.constant = contentSize.height
                break
            }
        }
    }
person Apfelsaft    schedule 13.05.2015

Быстрый ответ: следующий код вычисляет высоту вашего textView.

                let maximumLabelSize = CGSize(width: Double(textView.frame.size.width-100.0), height: DBL_MAX)
                let options = NSStringDrawingOptions.TruncatesLastVisibleLine | NSStringDrawingOptions.UsesLineFragmentOrigin
                let attribute = [NSFontAttributeName: textView.font!]
                let str = NSString(string: message)
                let labelBounds = str.boundingRectWithSize(maximumLabelSize,
                    options: NSStringDrawingOptions.UsesLineFragmentOrigin,
                    attributes: attribute,
                    context: nil)
                let myTextHeight = CGFloat(ceilf(Float(labelBounds.height)))

Теперь вы можете установить высоту вашего textView на myTextHeight

person RawMean    schedule 16.05.2015
comment
Извините, что это значит: let attribute = [NSFontAttributeName: textView.text.font!] Это действительно Swift-код без ошибок?) Вы имели в виду let attribute = [NSFontAttributeName: textView.font!] - person Massmaker; 17.06.2015

Вот ответ, если вам нужно динамически изменять размер textView и tableViewCell в staticTableView

[https://stackoverflow.com/a/43137182/5360675visible[1provided)

person Volodymyr Nazarkevych    schedule 31.03.2017

Работает как шарм на ios 11, и я работаю в ячейке, как ячейка чата с пузырем.

let content = UITextView(frame: CGRect(x: 4, y: 4, width: 0, height: 0))
content.text = "what ever short or long text you wanna try"
content.textAlignment = NSTextAlignment.left
content.font = UIFont.systemFont(ofSize: 13)
let spaceAvailable = 200 //My cell is fancy I have to calculate it...
let newSize = content.sizeThatFits(CGSize(width: CGFloat(spaceAvailable), height: CGFloat.greatestFiniteMagnitude))
content.isEditable = false
content.dataDetectorTypes = UIDataDetectorTypes.all
content.isScrollEnabled = false
content.backgroundColor = UIColor.clear
bkgView.addSubview(content)
person iOS Flow    schedule 25.09.2017
comment
Примечание. Этот ответ просто подчеркивает, что вам не нужно использовать CGFloat.greatestFiniteMagnitude, если вы ограничены определенной шириной или высотой - ›spaceAvailable в моем случае может быть максимальной шириной экрана - Ширина интервалов и других меток или минимальная ширина экрана все зависит от того, что я показываю ... - person iOS Flow; 12.07.2018

этот метод, кажется, работает для ios7

 // Code from apple developer forum - @Steve Krulewitz, @Mark Marszal, @Eric Silverberg
- (CGFloat)measureHeight
{
    if ([self respondsToSelector:@selector(snapshotViewAfterScreenUpdates:)])
    {
    CGRect frame = internalTextView.bounds;
    CGSize fudgeFactor;
    // The padding added around the text on iOS6 and iOS7 is different.
    fudgeFactor = CGSizeMake(10.0, 16.0);

    frame.size.height -= fudgeFactor.height;
    frame.size.width -= fudgeFactor.width;

    NSMutableAttributedString* textToMeasure;
    if(internalTextView.attributedText && internalTextView.attributedText.length > 0){
        textToMeasure = [[NSMutableAttributedString alloc] initWithAttributedString:internalTextView.attributedText];
    }
    else{
        textToMeasure = [[NSMutableAttributedString alloc] initWithString:internalTextView.text];
        [textToMeasure addAttribute:NSFontAttributeName value:internalTextView.font range:NSMakeRange(0, textToMeasure.length)];
    }

    if ([textToMeasure.string hasSuffix:@"\n"])
    {
        [textToMeasure appendAttributedString:[[NSAttributedString alloc] initWithString:@"-" attributes:@{NSFontAttributeName: internalTextView.font}]];
    }

    // NSAttributedString class method: boundingRectWithSize:options:context is
    // available only on ios7.0 sdk.
    CGRect size = [textToMeasure boundingRectWithSize:CGSizeMake(CGRectGetWidth(frame), MAXFLOAT)
                                              options:NSStringDrawingUsesLineFragmentOrigin
                                              context:nil];

    return CGRectGetHeight(size) + fudgeFactor.height;
}
else
{
    return self.internalTextView.contentSize.height;
}
}
person madmik3    schedule 29.09.2013

Самый простой способ задать UITextView - просто вызвать -sizeToFit, он должен работать и с scrollingEnabled = YES, после этого проверьте высоту и добавьте ограничение высоты в текстовое представление с тем же значением.
Обратите внимание, что UITexView содержит вставки, это означает, что вы не можете спросить строковый объект, сколько места он хочет использовать, потому что это всего лишь ограничивающий прямоугольник текста.
Все люди, которые испытывают неправильный размер, используя -sizeToFit, вероятно, из-за того, что текстовое представление еще не был выполнен макет для размера интерфейса.
Это всегда происходит, когда вы используете классы размера и UITableView, когда в первый раз создаются ячейки в - tableView:cellForRowAtIndexPath:, получается размер любой-любой конфигурации, если вы вычисляете Вы только что цените ширину текстового представления, отличную от ожидаемой, и это приведет к искажению всех размеров.
Чтобы решить эту проблему, я нашел полезным переопределить метод -layoutSubviews ячейки для пересчета высоты текстового представления.

person Andrea    schedule 22.10.2015

Если вы используете режим прокрутки и представление содержимого и хотите увеличить высоту в зависимости от высоты содержимого TextView, этот фрагмент кода вам поможет.

Надеюсь, это поможет, он отлично работал в iOS9.2

и, конечно же, установите textview.scrollEnabled = NO;

-(void)adjustHeightOfTextView
{
    //this pieces of code will helps to adjust the height of the uitextview View W.R.T content
    //This block of code work will calculate the height of the textview text content height and calculate the height for the whole content of the view to be displayed.Height --> 400 is fixed,it will change if you change anything in the storybord.


CGSize textViewSize = [self.textview sizeThatFits:CGSizeMake(self.view.frame.size.width, self.view.frame.size.height)];//calulcate the content width and height

float textviewContentheight =textViewSize.height;
self.scrollview.contentSize = CGSizeMake(self.textview.frame.size.width,textviewContentheight + 400);//height value is passed
self.scrollview.frame =CGRectMake(self.scrollview.frame.origin.x, self.scrollview.frame.origin.y, self.scrollview.frame.size.width, textviewContentheight+400);

CGRect Frame = self.contentview.frame;
Frame.size.height = textviewContentheight + 400;
[self.contentview setFrame:Frame];

self.textview.frame =CGRectMake(self.textview.frame.origin.x, self.textview.frame.origin.y, self.textview.frame.size.width, textviewContentheight);
[ self.textview setContentSize:CGSizeMake( self.textview.frame.size.width,textviewContentheight)];
self.contenview_heightConstraint.constant = 

self.scrollview.bounds.size.height;
    NSLog(@"%f",self.contenview_heightConstraint.constant);
}
person jithin    schedule 24.01.2016

Это довольно просто с наблюдением ключевого значения (KVO), просто создайте подкласс UITextView и выполните:

private func setup() { // Called from init or somewhere

    fitToContentObservations = [
        textView.observe(\.contentSize) { _, _ in
            self.invalidateIntrinsicContentSize()
        },
        // For some reason the content offset sometimes is non zero even though the frame is the same size as the content size.
        textView.observe(\.contentOffset) { _, _ in
            if self.contentOffset != .zero { // Need to check this to stop infinite loop
                self.contentOffset = .zero
            }
        }
    ]
}
public override var intrinsicContentSize: CGSize {
    return contentSize
}

Если вы не хотите создавать подклассы, вы можете попробовать выполнить textView.bounds = textView.contentSize в contentSize наблюдателе.

person Jonathan.    schedule 31.08.2019

Самым простым решением, которое сработало для меня, было наложить ограничение высоты на textView в раскадровке, а затем подключить textView и ограничение высоты к коду:

@IBOutlet var myAwesomeTextView: UITextView!
@IBOutlet var myAwesomeTextViewHeight: NSLayoutConstraint!

Затем, после настройки стилей текста и абзаца, добавьте это в viewDidAppear:

self.myAwesomeTextViewHeight.constant = self.myAwesomeTextView.contentSize.height

Некоторые примечания:

  1. В отличие от других решений, isScrollEnabled должно иметь значение true, чтобы это работало.
  2. В моем случае я устанавливал пользовательские атрибуты для шрифта внутри кода, поэтому мне пришлось установить высоту в viewDidAppear (до этого он не работал должным образом). Если вы не меняете какие-либо атрибуты текста в коде, вы сможете установить высоту в viewDidLoad или в любом другом месте после установки текста.
person iOS_Mouse    schedule 17.02.2021

Похоже, этот парень понял это для NSTextView, и его ответ применим и к iOS. Оказывается, intrinsicContentSize не синхронизируется с диспетчером компоновки. Если макет происходит после intrinsicContentSize, у вас может быть несоответствие.

Его легко исправить.

NSTextView в NSOutlineView с установкой IntrinsicContentSize неправильной высоты

person Hex Bob-omb    schedule 05.03.2021

Вот шаги

Решение работает на всех версиях iOS. Swift 3.0 и выше.

  1. Добавьте свой UITextView в View. Я добавляю это с помощью кода, вы можете добавить через конструктор интерфейсов.
  2. Добавьте ограничения. Я добавляю ограничения в код, вы также можете сделать это в конструкторе интерфейсов.
  3. Используйте UITextViewDelegate метод func textViewDidChange(_ textView: UITextView) для настройки размера TextView

Код:

  1. //1. Add your UITextView in ViewDidLoad
    let textView = UITextView()
    textView.frame = CGRect(x: 0, y: 0, width: 200, height: 100)
    textView.backgroundColor = .lightGray
    textView.text = "Here is some default text."
    
    
    
    //2. Add constraints
    textView.translatesAutoresizingMaskIntoConstraints = false
    [
    textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
    textView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
    textView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
    textView.heightAnchor.constraint(equalToConstant: 50),
    textView.widthAnchor.constraint(equalToConstant: 30)
    ].forEach{ $0.isActive = true }
    
    textView.font = UIFont.preferredFont(forTextStyle: .headline)
    
    textView.delegate = self
    textView.isScrollEnabled = false
    
    textViewDidChange(textView)
    
    
    //3. Implement the delegate method.
    func textViewDidChange(_ textView: UITextView) {
    let size = CGSize(width: view.frame.width, height: .infinity)
    let estimatedSize = textView.sizeThatFits(size)
    
    textView.constraints.forEach { (constraint) in
        if constraint.firstAttribute == .height {
            print("Height: ", estimatedSize.height)
            constraint.constant = estimatedSize.height
        }
    }
    }
    
person Vakas    schedule 19.07.2018

Не уверен, почему люди всегда слишком усложняют: вот оно:

- (void)textViewDidChange:(UITextView *)textView{ CGRect frame = textView.frame;
CGFloat height = [self measureHeightOfUITextView:textView];
CGFloat insets = textView.textContainerInset.top + textView.textContainerInset.bottom;
height += insets;
frame.size.height = height;

if(frame.size.height > textView.frame.size.height){
    CGFloat diff = frame.size.height - textView.frame.size.height;
    textView.frame = CGRectMake(5, textView.frame.origin.y - diff, textView.frame.size.width, frame.size.height);
}
else if(frame.size.height < textView.frame.size.height){
    CGFloat diff = textView.frame.size.height - frame.size.height;
    textView.frame = CGRectMake(5, textView.frame.origin.y + diff, textView.frame.size.width, frame.size.height);
}
[textView setNeedsDisplay];
}
person Gal Blank    schedule 23.12.2014