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

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

"Hello world"

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

"Goodbye world"

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

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

Ответы

Ответ 1

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

Ответ 2

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

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

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

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

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

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

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


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

[self.textView sizeToFit];

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

enter image description here

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

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

Он исправляет проблему "дополнительной строки" на 6 +.

Ответ 3

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

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

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

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

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

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

Ответ 4

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

По коду

textView.scrollEnabled = false

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

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

enter image description here

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

Ответ 5

Swift:

textView.sizeToFit()

Ответ 6

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

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

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

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

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

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

Ответ 7

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

Ответ 8

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

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

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

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

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

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

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

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

Последнее, что нужно сделать, - сказать суперклассу updateConstraints.

[super updateConstraints];

Теперь наш класс выглядит следующим образом:

ResizableTextView.m

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

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

    [super updateConstraints];
}

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

Но подождите! Y NO ANIMATION!

Вы можете легко анимировать изменения, чтобы сделать 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];

Ответ 9

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

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

Ответ 10

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

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

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

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

Ответ 11

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

Ответ 12

Достаточно вещей:

  • Не забудьте установить прокрутку НЕТ для UITextView:

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

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

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

Ответ 13

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

  • Установить ограничения высоты для 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;
    }];

}

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

Ответ 14

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

[myTextView setDelegate: self];

...

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

Ответ 15

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

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

Ответ 16

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

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

[_textView sizeToFit];

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

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

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

Ответ 17

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

  • В UITextView должна быть отключена прокрутка, которую вы можете выполнять программно или через конструктор интерфейса. Он не будет изменяться, если прокрутка включена, потому что прокрутка позволяет просматривать более крупный контент.
  • В viewDidLoad для UITableViewController вы должны установить значение для оценочногоRowHeight, а затем установить rowHeight в UITableViewAutomaticDimension.

- (void)viewDidLoad {
    [super viewDidLoad];
    self.tableView.estimatedRowHeight = self.tableView.rowHeight;
    self.tableView.rowHeight = UITableViewAutomaticDimension;
}
  1. Цель развертывания проекта должна быть iOS 8 или выше.

Ответ 18

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

Ответ 19

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

Ответ 20

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

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

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

Выходы:

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

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

Ответ 21

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

добавлять константы

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

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

Ответ 22

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

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

Ответ 23

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

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

Ответ 24

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

И это... Cheers

Ответ 25

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

Ответ 26

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

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

Ответ 27

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

Ответ 28

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

Ответ 29

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

Ответ 30

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

[fooobar.com/info/9519/...