Как мне определить размер 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
к его правильной высоте содержания. Это можно сделать с помощью 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+.
Очень простое в использовании решение с использованием кода и раскадровки.
По коду
textView.scrollEnabled = false
По раскадровке
Снимите флажок Включить прокрутку
Не нужно ничего делать кроме этого.
Для динамического определения размера UITextView
внутри UITableViewCell
Я обнаружил, что следующая комбинация работает в Xcode 6 с iOS 8 SDK:
- Добавить
UITextView
кUITableViewCell
и ограничить его в стороны - Установить
UITextView
"sscrollEnabled
собственность на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:
По моему (ограниченному) опыту,
- (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
Это возвращает размер прямоугольника, который соответствует данной строке с данным шрифтом. Передайте размер с желаемой шириной и максимальной высотой, а затем вы можете посмотреть на высоту, возвращаемую в соответствии с текстом. Существует версия, которая позволяет также указать режим разрыва строки.
Затем вы можете использовать возвращенный размер, чтобы изменить размер вашего представления, чтобы соответствовать.
Мы можем сделать это с помощью ограничений.
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, который демонстрирует это в действии, но вот ключ:
- У UITextView должна быть отключена прокрутка, которую вы можете сделать программно или через конструктор интерфейса. Размер не изменится, если включена прокрутка, потому что прокрутка позволяет просматривать содержимое большего размера.
- В viewDidLoad для UITableViewController необходимо установить значение для timaRowHeight, а затем установить
rowHeight
вUITableViewAutomaticDimension
,
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.estimatedRowHeight = self.tableView.rowHeight;
self.tableView.rowHeight = UITableViewAutomaticDimension;
}
- Цель развертывания проекта должна быть 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.
}
}
Достаточно следующих вещей:
- Просто помните, что для вашей прокрутки нужно включить NO
UITextView
:
- Правильно установите ограничения 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
Единственный код, который будет работать, - это тот, который использует "SizeToFit", как в ответе jhibberd выше, но на самом деле он не подхватит, если вы не вызовете его в ViewDidAppear или не подключите его к событию UITextView текстовое изменение.