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

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

"Hello world"

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

"Goodbye world"

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

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

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

Juan Boero 18.12.2015 22:43

почему нет вопросов, как это сделать нединамически? Я даже не могу понять, как это сделать статично. И предоставленные решения, похоже, не отменяют «ограничений» в раскадровке.

pete 14.08.2020 06:27
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
536
2
390 960
41
Перейти к ответу Данный вопрос помечен как решенный

Ответы 41

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

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

sizetofit спасает положение

michaelsnowden 24.06.2014 13:36

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

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

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

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

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

[myTextView setDelegate: self];

...

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

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

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

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

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

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

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

Он получает размер, как если бы он рисовал текст на UILabel. Если вы установите этот размер для фрейма uitextview, тогда у вас все равно будет необходимая прокрутка.

Kevlar 04.12.2009 22:01

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

- (void)textViewDidChange:(UITextView *)textView {
  CGSize textSize = textview.contentSize;
  if (textSize != textView.frame.size)
      textView.frame.size = textSize;
}

Это не работает! Это вызывает ошибку: "lvalue требуется как левый операнд присваивания"

leviathan 07.06.2010 20:03

Можно назначить только textView.frame.

jweyrich 01.03.2013 22:30

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

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

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+.

Ты спас мне день. Я боролся с расширением функции класса selfCalculating, забывая об этом. Спасибо.

Lukasz 05.01.2011 13:52

@Ronnie, спасибо за ваш код, я использую ваш код, но когда мое содержимое textview достигает строки 2000, этот код замедляет работу моего приложения, вы можете сказать мне, почему?

R. Dewi 28.01.2011 10:52

@Ronnie, где мы должны писать этот код. Я хотел бы сначала отобразить одну строку кадра, и по мере того, как типы пользователей в кадре должны увеличиваться, чтобы разместить текст без прокрутки вверх.

Yogesh 28.04.2011 08:03

Любопытнее: когда я уменьшаю текст, так что contentSize имеет высоту всего 79, добавляя 100, я получаю только 1 строку. Мне нужно добавить 200 - установить высоту UITextView на 279 - чтобы отобразить 3 строки.

Hot Licks 23.06.2011 21:42

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

Hot Licks 23.06.2011 21:52

Использование [_textView sizeToFit] обновляет свойство contentSize, удаляя требование заранее добавлять его в scrollView.

Rodrigo 22.11.2011 06:15

Вы также можете добавить frame.size.height + = (_textView.contentInset.top + _textView.contentInset.bottom); Если вы изменили contentInset, чтобы не оставлять пустую рамку вокруг текста.

Maciej Swic 27.12.2012 19:53

_textView.scrollEnabled = НЕТ; тоже нужен. В противном случае я заканчивал смещение содержимого после новой строки.

Bo. 15.03.2013 15:07

Теперь, когда вы это сказали, это так очевидно. Я пытался рассчитать высоту с помощью sizeWithFont:forWidth:lineBreakMode, спасибо!

Adam Carter 26.03.2013 23:47

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

Xander Dunn 15.04.2013 08:29

Если вы используете автоматическое размещение, вызовите layoutIfNeeded в супервизоре. Затем вы можете взять высоту из contentSize.

phatmann 21.05.2013 08:46

Если вы заставили его работать на iOS 7, помогите, пожалуйста, в этом вопросе: stackoverflow.com/questions/19049532/…

Henrik Erlandsson 27.09.2013 15:25

Однако речь идет об изменении размера внешнего контейнера. Это поведение изменилось в iOS 7. В связанном вопросе я показываю, где добавить sizeToFit и layoutIfNeeded.

Henrik Erlandsson 21.10.2013 10:25

@HenrikErlandsson, ваш комментарий сэкономил мне сейчас много работы :)

giorashc 17.11.2013 23:44

У меня это не работает в 2016 году. Возможно, устарело. Высота содержимого всегда равна высоте кадра, даже когда я добавил addSubView.

zztop 20.05.2016 23:46

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

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

Здесь '5' в координате Y UILabel - это промежуток, который я хочу между textView и Label.

dheeraj 14.03.2011 13:15

если сюда попадет какой-либо другой, это решение сработает для меня, 1 «Ронни Лью» +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;

И это ... Ура

Это отлично сработало, когда мне нужно было подогнать текст в 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);
}

Если у вас нет под рукой 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; это добавляет пустую строку под вашим текстом, что может выглядеть лучше, если есть визуально тяжелые элементы управления непосредственно под текстовым представлением.

отличный результат в iOS 8.1

Logic 31.03.2015 10:43

Прокладка была для меня ключом

thibaut noah 10.12.2016 15:48

Лучший способ, который я обнаружил, изменить размер высоты 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];

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

Где я могу прочитать об этом подробнее? Я совершенно не понимаю, как выглядит новое поведение в iOS 7 по этому поводу.

Stian Høiland 19.09.2013 19:13

Что означает переменная CGSize size?

Tchelow 02.11.2013 14:20

Я снял размер, его там не должно было быть.

phatmann 26.11.2013 01:00

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

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

Ответ принят как подходящий

Это работает как для 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;

Протестировано на iOS 7 для изменения размера UITextView в UITableViewCell. Работает действительно здорово. Это единственный ответ, который, наконец, сработал для меня. Спасибо!

Alex 30.11.2013 22:40

Мне был нужен textview.scrollEnabled = NO;, чтобы текст не обрезался даже в iOS 7.

Brian 07.01.2014 02:06

Я рекомендую использовать макрос CGFLOAT_MAX вместо MAXFLOAT. Правильно на обеих 32/64 битных платформах.

eonil 27.08.2014 18:18

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

scientiffic 07.09.2014 20:42

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

user3344977 30.11.2014 00:16

Также вы можете использовать INFINITY вместо CGFLOAT_MAX и MAXFLOAT. Выглядит смешнее

fnc12 27.01.2015 11:55

Тот, кто сталкивается с небольшими колебаниями перед вводом в следующую строку, добавляет этот `NSRange bottom = NSMakeRange (textView.text.length -1, 1); [textView scrollRangeToVisible: внизу]; `

ajay_nasa 28.08.2015 13:27

Сработало отлично !! ПРИМЕЧАНИЕ для использования Autolayout: мне пришлось сначала установить верхнее / нижнее пространство для ограничений супервизора, чтобы это работало.

Fateh Khalsa 14.09.2015 21:57

Почему в версии Swift есть два отдельных вызова sizeThatFits (...)? Для чего первая строчка?

nekonari 05.05.2016 18:05

Если у кого-то есть проблема с изменением размера, поместите этот код выше, чтобы переопределить метод func layoutSubviews ().

Daniel Kuta 12.08.2016 15:04

Это прекрасно работает. Был сбит с толку на несколько минут, потому что у меня все еще было установлено ограничение автопложности высоты в IB для текстового представления, но после его удаления все в порядке.

jbll 06.10.2016 03:40

в этом методе есть ошибка в ios 9. размер текстового представления достигает нормальной высоты при выборе текста из текстового представления

Prashant Ghimire 26.10.2016 10:26

Просто не забудьте добавить это в viewDidLayoutSubviews, а не в viewWillAppear

Derek 23.01.2017 15:34

Когда textview.scrollEnabled = NO; Мой курсор не виден, после ввода первого символа он становится видимым. Когда прокрутка включена, при вводе текста верхний текст прокручивается слишком сильно (чрезмерная прокрутка), после ввода первого символа прокрутка исправляется сама. Поэтому оставление прокрутки включенной и использование этой строки исправляет это для меня: NSRange bottom = NSMakeRange (textView.text.length -1, 1); [textView scrollRangeToVisible: внизу]; Как написал aiay_nasa.

Wayne 04.09.2017 10:20

Это отлично работает, просто не забудьте убедиться, что у вас не установлены ограничения по высоте для TextView в построителе интерфейса.

velval 30.05.2018 01:48

Размер отлично выглядит в отладчике пользовательского интерфейса, но текст не переносится - я поместил это в initWithFrame, может быть, в этом проблема?

ArielSD 03.05.2019 01:24

В качестве альтернативы установите ограничение высоты textView равным расчетной высоте.

JLundell 17.08.2019 20:47

Что касается предложения @ DanielKuta, обратите внимание, что layoutSubviews () - это функция UIView, отлично подходит, если вы подклассифицируете текстовое представление или родительский объект. Если вы делаете это в контроллере представления, вам нужно viewWillLayoutSubviews ().

JLundell 17.08.2019 20:55

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

PakitoV 11.06.2020 16:52

Я не думаю, что это может преодолеть ограничения. Как указать это изначально в ограничении раскадровки? У меня даже нет динамического текста; просто статично.

pete 14.08.2020 06:45

этот метод, кажется, работает для 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;
}
}

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

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

Используйте CGFLOAT_MAX, а не FLT_MAX.

rob mayoff 23.09.2014 23:12

Хотел бы я дать вам больше одного голоса за это потрясающее решение !!

Scooter 03.10.2014 06:54

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

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

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

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

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

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

Лучший ответ для меня. Работал отлично, и UITableViewAutomaticDimension был добавлен в iOS 5, поэтому он обратно совместим.

smmelzer 09.04.2015 17:23

У меня не совсем сработало. Метод textViewDidChange заставляет мое представление таблицы подпрыгивать каждый раз, когда я набираю символ. Я предлагаю вместо использования этого метода в методе tableView: cellForRowAtIndexPath для строки с текстовым представлением на ней, если вы находитесь в режиме только для чтения, для параметра .scrollEnabled установлено значение NO, и если вы находитесь в режиме редактирования, установленном прокрутите до ДА. Таким образом, при простом отображении он всегда будет показывать полный текст, а при редактировании он будет начинаться с полного текста, и при добавлении следующего текста он останется того же размера, а предыдущий текст будет прокручиваться сверху.

SimonTheDiver 18.12.2015 13:57

кроме того, я добавил ограничение высоты> = в текстовое поле, так как пустое текстовое поле не имело высоты и его нельзя было нажать, чтобы начать редактирование.

SimonTheDiver 18.12.2015 14:04

Отличное решение. Для меня tableView.rowHeight = UITableViewAutomaticDimension был не нужен.

Christopher Pickslay 21.01.2016 05:13

Еще раз спасибо, Бретт Дональд! Я забыл, как это сделать, и через год снова нашел ваш ответ ?.

Eric Conner 01.12.2016 04:26

Для всех, кто столкнется с этим ответом в будущем, главное, что вам нужно знать, это просто UITextView.scrollEnabled = @NO

Brett Donald 05.01.2017 03:51

Это заставляет экран подпрыгивать каждый раз при обновлении, как сказал @SimonTheDiver

Stefanija 22.02.2018 16:50

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

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

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

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

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

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

[_textView sizeToFit];

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

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

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

вот быстрая версия @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

Вопрос действительно зависит от языка. Действительно ли необходимо возвращаться и писать быстрые порты для всех ответов на вопросы, связанные с UIKit? Похоже, это способствует шумному разговору.

eremzeit 05.04.2015 02:55

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

Fareed Alnamrouti 05.04.2015 03:14

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

Основываясь на ответе Никиты Тука, я пришел к следующему решению в 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
            }
        }
    }

Быстрый ответ: Следующий код вычисляет высоту вашего 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.

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

Massmaker 17.06.2015 12:14

Начиная с 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 или выше.

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

Antzi 30.06.2015 17:12

Быстрый :

textView.sizeToFit()

Кроме того, я обнаружил, что мне нужно установить для параметра scrolling enabled значение false, чтобы это работало. textView.scrollEnabled = false

tmarkiewicz 19.03.2016 21:52

Не забудьте вызвать textView.layoutIfNeeded () после textView.sizeToFit ()

Daniel Kuta 01.06.2016 14:24

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

Francisco Romero 03.10.2016 17:48

Это ответ !!! Нет необходимости в более сложных решениях !!! @FranciscoRomero Почему бы не поместить текстовое представление в ячейку таблицы или ячейку представления коллекций, тогда вы всегда можете прокрутить таблицу или представление коллекций ....

Ben Smith 05.02.2019 14:36

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

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

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

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

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

Если вы используете режим прокрутки и представление содержимого и хотите увеличить высоту в зависимости от высоты содержимого 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);
}

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

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

}

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

это ФАНТАСТИЧЕСКИЙ ник!

Fattie 19.02.2017 23:59

За установкой константы должен следовать [textView layoutIfNeeded]; в блоке анимации. (Анимация автоматического раскладки делает это именно так.)

Will Larche 05.04.2017 05:19

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

По коду

textView.scrollEnabled = false

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

Снимите флажок Прокрутка включить

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

Это правильный ответ. В StoryBoard у вас есть TextView AutoSize, как и в UILabel. Все, что нам нужно сделать, это установить scrollEnabled = false.

pnavk 13.08.2016 00:01

По сути, это правильно: если вы используете ограничения Autolayout и отключаете прокрутку, все работает.

Dan Rosenstark 10.04.2017 17:38

Большое спасибо. Это было так просто, просто отключите прокрутку в раскадровке или коде, это будет как UILabel со строками 0.

samir105 10.03.2018 10:24

@Mansuu .... Это не гарантированное решение, так как есть вещи, которые вы можете сделать, чтобы переопределить внутренний размер содержимого, и если вы измените текст после размещения представления, он не будет обновляться, если вы не установите это тоже вверх. Но эти вещи остаются верными и для установки numberOfLines = 0 в текстовом поле. Если это не сработает, вероятно, у вас есть другие ограничения, которые неявно устанавливают высоту.

Jake T. 05.07.2018 20:15

Ваш ответ приемлемый, но если мы добавим ограничение по высоте, это не сработает.

iOS 28.08.2018 09:31

@iOS, если вы добавляете ограничение по высоте, сделайте приоритет ограничений ниже, иначе это не сработает.

Alok 28.08.2018 10:21

Объяснение, почему это работает: stackoverflow.com/a/65861960/815742

fumoboy007 23.01.2021 20:07

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

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

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

Выходы:

метод делегата не вызывается @Peter Stajger

Mansuu.... 29.08.2017 13:24

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

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

Работает как шарм на 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)

Примечание. Таким образом, этот ответ просто подчеркивает, что вам не нужно использовать CGFloat.greatestFiniteMagnitude, если вы ограничены определенной шириной или высотой -> spaceAvailable в моем случае может быть максимальной шириной экрана - Интервалы и ширина других меток или минимальная ширина экрана все зависит от того, что я показываю ...

iOS Flow 12.07.2018 04:25

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

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

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

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

Вот шаги

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

  1. Добавьте свой UITextView к View. Я добавляю это с помощью кода, вы можете добавить через конструктор интерфейсов.
  2. Добавьте ограничения. Я добавляю ограничения в код, вы также можете сделать это в конструкторе интерфейсов.
  3. Используйте метод UITextViewDelegatefunc 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
        }
    }
    }
    

Это довольно просто с наблюдением ключевого значения (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.

Это отлично работает для 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)
}

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

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

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

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

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

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

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

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

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

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

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

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