¿Cómo dimensiono un UITextView a su contenido en iOS 7?

He estado usando la respuesta aceptada aquí durante años.

En iOS 7, contentSize.height se convierte en frame.height-8, independientemente del contenido de texto.

¿Cuál es un método de trabajo para ajustar la altura en iOS 7?

Estoy a favor de este cambio mínimo de código: solo agregue estas dos líneas después de agregar addSubview y antes de obtener la height del frame

 ... [scrollView1 addSubview: myTextView]; [myTextView sizeToFit]; //added [myTextView layoutIfNeeded]; //added CGRect frame = myTextView.frame; ... 

Esto se prueba compatible con versiones anteriores de iOS 6. TENGA en cuenta que reduce el ancho. Si solo está interesado en la altura y tiene un ancho fijo, simplemente tome la nueva altura pero configure el ancho original, y funciona igual que antes en iOS 6 y 7.

(Especulación: también tiene el tamaño adecuado para iOS 7, pero el diseño se actualiza más adelante o en un hilo separado, y esto fuerza el diseño inmediatamente para que su marco se actualice a tiempo para usar su valor de altura unas pocas líneas después en el mismo hilo)

NOTAS:

1) Puede haber implementado el tamaño del contenedor externo de esta manera o no. Sin embargo, parece ser un fragmento común, y lo he usado en mis proyectos.

2) Dado que sizeToFit parece funcionar como se esperaba en iOS 7, es probable que no necesite el addSubView prematuro. Si todavía funcionará en iOS 6, entonces no lo probé.

3) La especulación: el layoutIfNeeded extra layoutIfNeeded medio hilo podría ser costoso. La alternativa que veo es cambiar el tamaño del contenedor externo en la callback del diseño (activada o no dependiendo de si el sistema operativo decide si el diseño es necesario o no) donde el tamaño externo del contenedor causará otra actualización del diseño. Ambas actualizaciones se pueden combinar con otras actualizaciones de diseño para ser más eficientes. Si tiene una solución así y puede demostrar que es más eficiente, agréguela como respuesta y me aseguraré de mencionarla aquí.

Como uso el diseño automático, uso el valor de [textView sizeThatFits:CGSizeMake(textView.frame.size.width, CGFLOAT_MAX)].height para actualizar la constant de la altura del UILayoutConstraint .

Utilizo una versión adaptada de la respuesta de madmik que elimina el factor de dulce de azúcar:

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

Basado en otras respuestas, lo hice funcionar (en Swift). Esto resuelve el problema con el carácter de nueva línea.

 textView.sizeToFit() textView.layoutIfNeeded() let height = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max)).height textView.contentSize.height = height 

Diseño automático es necesario.

Si está utilizando Diseño automático, puede crear una subclase de UITextView trivial que autoevalúa la altura de la vista de texto para que se ajuste al contenido:

 @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 

Por supuesto, el ancho y la posición de la vista de texto deben definirse mediante restricciones adicionales configuradas en otras partes del progtwig.

Si crea esta vista de texto personalizada en IB, otorgue a la vista de texto una restricción de altura para satisfacer a Xcode; solo asegúrese de que la restricción de altura creada en IB sea simplemente un marcador de posición (es decir, marque la casilla que dice “Eliminar en tiempo de comstackción”).

enter image description here

Una forma alternativa de implementar la subclase UITextView es la siguiente (esta implementación podría calificar como la mejor práctica):

 @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 

Si está utilizando el diseño automático, puede usar la siguiente subclase UITextView que agrega una altura intrínseca:

 @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 

este método parece funcionar.

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

Si usa iOS 7 o superior, puede activar el diseño automático, fijar cada lado de la vista de texto al borde de su vista principal y funciona bien. No se requiere código adicional.

En iOS 8, heredarás algo de offset de contenido del padre, del cual también debes deshacerte.

Un ejemplo de subclase

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

En el guión gráfico, si usa restricciones, asegúrese de estar limitado a su super visión en la pestaña ‘regla’ del panel derecho en xcode para UITextView. Mi problema fue que tenía una restricción de -80 pts en el ‘Espacio posterior a’.

enter image description here

Chicos que usan el autolayout y su sizetofit no funciona, entonces verifiquen su restricción de ancho una vez. Si te perdiste la restricción de ancho, entonces la altura será precisa.

No es necesario usar ninguna otra API. solo una línea arreglaría todo el problema.

 [_textView sizeToFit]; 

Aquí, solo me preocupaba la altura, mantener el ancho fijo y no tener la restricción de ancho de mi TextView en el guión gráfico.

Y esto fue para mostrar el contenido dynamic de los servicios.

Espero que esto pueda ayudar …

Escribí una categoría sobre UITextView :

 - (CGSize)intrinsicContentSize { return self.contentSize; } - (void)setContentSize:(CGSize)contentSize { [super setContentSize:contentSize]; [self invalidateIntrinsicContentSize]; } 

Cuando UIKit establece su intrinsic content size contentSize , UITextView ajusta su intrinsic content size . Eso funciona muy bien con el autolayout .

La respuesta dada por bilobatum funcionó perfectamente con el diseño automático, es decir, la subclasificación de la vista de texto.

Si desea limitar el alto de la vista de texto, agregue otra restricción (la agregué usando el guión gráfico, es decir, altura <= 166 (altura según su necesidad))

Luego, dentro de la subclase, reduzca la prioridad de la restricción de altura a 750 (self.heightConstraint.priority = 750) para evitar el conflicto entre la restricción de altura agregada en la subclase y la restricción de altura agregada en el guión gráfico.