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

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

"Hello world"

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

"Goodbye world"

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

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

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;

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

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

По коду

textView.scrollEnabled = false

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

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

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

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

  • Добавить UITextView к UITableViewCell и ограничить его в стороны
  • Установить UITextView"s scrollEnabled собственность на 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:

Свифт:

textView.sizeToFit()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. Установите ограничения высоты для UITextView.

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

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

3. не забудьте установить делегат для вашего textview.

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

}

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

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

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

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

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

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

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

Выходы:

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

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

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

[myTextView setDelegate: self];

...

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

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

  1. Просто помните, что для вашей прокрутки нужно включить NO UITextView:

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

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

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

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

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

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

[_textView sizeToFit];

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

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

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

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

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

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

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

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

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

- (void) reduFontToFit: (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(@"шрифт до%.1f от%.1f", pointSize, tv.font.pointSize);
}

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

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

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

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

Если кто-нибудь еще доберется до меня, это решение сработает для меня, 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 в соответствии с размером текста.

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

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

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

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

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

[ /questions/21806150/izmenenie-razmera-tekstovogo-predstavleniya-i-yachejki-tablichnogo-predstavleniya-v-staticheskom-tablichnom-predstavlenii/21806168#21806168

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

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