Как настроить UITextView на его содержимое на iOS 7?
Я использовал принятый ответ здесь в течение многих лет.
В iOS 7 contentSize.height становится frame.height-8, независимо от текстового содержимого.
Какой рабочий способ настроить высоту на iOS 7?
Ответы
Ответ 1
Я рекомендую это минимальное изменение кода: просто добавьте эти две строки после addSubview
и перед тем, как захватить height
frame
...
[scrollView1 addSubview: myTextView];
[myTextView sizeToFit]; //added
[myTextView layoutIfNeeded]; //added
CGRect frame = myTextView.frame;
...
Это проверено обратно совместимо с iOS 6. ПРИМЕЧАНИЕ, которое оно сокращает ширину. Если вас просто интересует высота и имеет фиксированную ширину, просто возьмите новую высоту, но установите исходную ширину, и она работает так же, как и на iOS 6 и 7.
(Спекуляция: размер тоже подходит для iOS 7, но макет обновляется позже или в отдельном потоке, и это мгновенно заставляет макет, чтобы его фрейм обновлялся вовремя для использования его значения высоты a несколько строк позже в том же потоке.)
ПРИМЕЧАНИЯ:
1) Вы могли бы или не могли бы реализовать внешний размер контейнера таким образом. Однако, похоже, это общий фрагмент, и я использовал его в своих проектах.
2) Так как sizeToFit
работает как ожидалось на iOS 7, вам, скорее всего, не нужен преждевременный addSubView. Будет ли он по-прежнему работать на iOS 6, то не проверяется мной.
3) Спекуляция: дополнительная layoutIfNeeded
средняя нить может быть дорогостоящей. Альтернативой, как я вижу, является изменение размера внешнего контейнера на обратном вызове макета (запущен или не зависит от того, решит ли ОС, нужен ли макет или нет), где внешний размер внешнего контейнера приведет к другому обновлению макета. Оба обновления могут быть объединены с другими обновлениями макета, чтобы быть более эффективными. Если у вас есть такое решение, и вы можете показать, что оно более эффективно, добавьте его в качестве ответа, и я обязательно укажу его здесь.
Ответ 2
Поскольку я использую Auto Layout, я использую значение [textView sizeThatFits:CGSizeMake(textView.frame.size.width, CGFLOAT_MAX)].height
для обновления constant
высоты textView
UILayoutConstraint
.
Ответ 3
Я использую адаптированную версию ответа madmik, которая устраняет фактор выдумки:
- (CGFloat)measureHeightOfUITextView:(UITextView *)textView
{
if ([textView respondsToSelector:@selector(snapshotViewAfterScreenUpdates:)])
{
// This is the code for iOS 7. contentSize no longer returns the correct value, so
// we have to calculate it.
//
// This is partly borrowed from HPGrowingTextView, but I've replaced the
// magic fudge factors with the calculated values (having worked out where
// they came from)
CGRect frame = textView.bounds;
// Take account of the padding added around the text.
UIEdgeInsets textContainerInsets = textView.textContainerInset;
UIEdgeInsets contentInsets = textView.contentInset;
CGFloat leftRightPadding = textContainerInsets.left + textContainerInsets.right + textView.textContainer.lineFragmentPadding * 2 + contentInsets.left + contentInsets.right;
CGFloat topBottomPadding = textContainerInsets.top + textContainerInsets.bottom + contentInsets.top + contentInsets.bottom;
frame.size.width -= leftRightPadding;
frame.size.height -= topBottomPadding;
NSString *textToMeasure = textView.text;
if ([textToMeasure hasSuffix:@"\n"])
{
textToMeasure = [NSString stringWithFormat:@"%@-", textView.text];
}
// NSString class method: boundingRectWithSize:options:attributes:context is
// available only on ios7.0 sdk.
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
[paragraphStyle setLineBreakMode:NSLineBreakByWordWrapping];
NSDictionary *attributes = @{ NSFontAttributeName: textView.font, NSParagraphStyleAttributeName : paragraphStyle };
CGRect size = [textToMeasure boundingRectWithSize:CGSizeMake(CGRectGetWidth(frame), MAXFLOAT)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:attributes
context:nil];
CGFloat measuredHeight = ceilf(CGRectGetHeight(size) + topBottomPadding);
return measuredHeight;
}
else
{
return textView.contentSize.height;
}
}
Ответ 4
Основываясь на других ответах, я заставил его работать (в Swift).
Это решает проблему с символом новой строки.
textView.sizeToFit()
textView.layoutIfNeeded()
let height = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max)).height
textView.contentSize.height = height
Требуется автоматическая компоновка.
Ответ 5
Если вы используете автоматическую компоновку, вы можете создать тривиальный подкласс UITextView
, который автоматически подгоняет высоту текстового вида для соответствия содержимому:
@interface ContentHeightTextView : UITextView
@end
@interface ContentHeightTextView ()
@property (nonatomic, strong) NSLayoutConstraint *heightConstraint;
@end
@implementation ContentHeightTextView
- (void)layoutSubviews
{
[super layoutSubviews];
CGSize size = [self sizeThatFits:CGSizeMake(self.bounds.size.width, FLT_MAX)];
if (!self.heightConstraint) {
self.heightConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:1.0f constant:size.height];
[self addConstraint:self.heightConstraint];
}
self.heightConstraint.constant = size.height;
[super layoutSubviews];
}
@end
Конечно, ширина и позиция просмотра текста должны определяться дополнительными ограничениями, настроенными в другом месте программы.
Если вы создаете это настраиваемое текстовое представление в IB, дайте текстовому представлению ограничение высоты, чтобы удовлетворить Xcode; просто убедитесь, что ограничение высоты, созданное в IB, является просто заполнителем (т.е. отметьте поле с надписью "Удалить во время сборки" ).
Альтернативный способ реализации подкласса UITextView
заключается в следующем (эта реализация может квалифицироваться как лучшая практика):
@interface ContentHeightTextView ()
@property (nonatomic, strong) NSLayoutConstraint *heightConstraint;
@end
@implementation ContentHeightTextView
- (void)layoutSubviews
{
[super layoutSubviews];
[self setNeedsUpdateConstraints];
}
- (void)updateConstraints
{
CGSize size = [self sizeThatFits:CGSizeMake(self.bounds.size.width, FLT_MAX)];
if (!self.heightConstraint) {
self.heightConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:1.0f constant:size.height];
[self addConstraint:self.heightConstraint];
}
self.heightConstraint.constant = size.height;
[super updateConstraints];
}
@end
Ответ 6
Если вы используете автоматическую компоновку, вы можете использовать следующий подкласс UITextView, который добавляет внутреннюю высоту:
@implementation SelfSizingTextView
- (void)setText:(NSString *)text
{
[super setText:text];
[self invalidateIntrinsicContentSize];
}
- (void)setFont:(UIFont *)font
{
[super setFont:font];
[self invalidateIntrinsicContentSize];
}
- (CGSize)intrinsicContentSize
{
CGFloat width = self.frame.size.width;
CGSize size = [self sizeThatFits:CGSizeMake(width, MAXFLOAT)];
return CGSizeMake(UIViewNoIntrinsicMetric, size.height);
}
@end
Ответ 7
этот метод работает.
// 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;
}
}
Ответ 8
Если вы используете iOS 7+, вы можете просто включить автоматический макет, привязать каждую сторону текстового вида к краю своего родительского представления, и он отлично работает. Дополнительный код не требуется.
Ответ 9
В iOS 8 вы наследуете некоторое смещение содержимого от родителя, от которого вам также нужно избавиться.
Пример подкласса
// Originally from https://github.com/Nikita2k/resizableTextView
#import "ResizableTextView.h"
@implementation ResizableTextView
- (void) updateConstraints {
// calculate contentSize manually
// ios7 doesn't calculate it before viewDidAppear and we'll get here before
CGSize contentSize = [self sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)];
// set the height constraint to change textView height
[self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
if (constraint.firstAttribute == NSLayoutAttributeHeight) {
constraint.constant = contentSize.height;
*stop = YES;
}
}];
[super updateConstraints];
}
- (void)setContentOffset:(CGPoint)contentOffset
{
// In iOS 8 we seem to be inheriting the content offset from the parent.
// I'm not interested
}
@end
Ответ 10
В раскадровке, используя ограничения, убедитесь, что вы ограничены своим супервизором на вкладке "линейка" правой панели на xcode для UITextView. Моя проблема заключалась в том, что у меня было ограничение -80 очков на "трейлинг-пространстве".
Ответ 11
Ребята, использующие автозапуск и ваш sizetofit, не работают, пожалуйста, проверьте один раз ограничение ширины. Если вы пропустили ограничение ширины, высота будет точной.
Не нужно использовать какой-либо другой API. только одна строка исправит всю проблему.
[_textView sizeToFit];
Здесь меня беспокоила только высота, сохраняя фиксированную ширину и пропустив ограничение ширины моего TextView в раскадровке.
И это должно было показать динамический контент из сервисов.
Надеюсь, это поможет.
Ответ 12
Я написал категорию над UITextView
:
- (CGSize)intrinsicContentSize {
return self.contentSize;
}
- (void)setContentSize:(CGSize)contentSize {
[super setContentSize:contentSize];
[self invalidateIntrinsicContentSize];
}
Когда UIKit
устанавливает свой contentSize
, UITextView
настраивает intrinsic content size
. Это хорошо работает с autolayout
.
Ответ 13
Ответ, заданный bilobatum, отлично работал с авто-макетом, т.е. подклассифицировал текст.
Если вы хотите ограничить высоту текстового вида, добавьте другое ограничение (я добавил его с помощью раскадровки, то есть высоты <= 166 (высота согласно вашей потребности))
Затем внутри подкласса уменьшите приоритет ограничения высоты до 750 (self.heightConstraint.priority = 750), чтобы избежать конфликта между ограничениями высоты, добавленными в подкласс и ограничение высоты, добавленное в раскадровку.