ios uitextview autolayout nslayoutconstraint

ios - UITextView que se expande al texto usando el diseño automático



autolayout nslayoutconstraint (12)

Acabo de entrar en este tema, no sé si estoy fuera de tema, pero si configuras tu UITextView como "no" desplazable:

[textView setScrollEnabled:NO];

podrá calcular un tamaño intrínseco, permitiendo así que el diseño automático mueva los otros elementos por sí mismo, luego depende de usted configurar las restricciones correctas.

(en iOS 7)

Tengo una vista que se presenta completamente mediante el diseño automático programáticamente. Tengo una UITextView en el medio de la vista con elementos arriba y debajo. Todo funciona bien, pero quiero poder expandir UITextView a medida que se agrega texto. Esto debería empujar todo debajo de él hacia abajo a medida que se expande.

Sé cómo hacer esto de la manera "springs and struts", pero ¿hay una forma de diseño automático de hacer esto? La única forma en que puedo pensar es eliminando y volviendo a agregar la restricción cada vez que necesite crecer.


Aquí hay una solución para las personas que prefieren hacerlo todo por diseño automático:

En el Inspector de Tamaño:

  1. Establezca la prioridad de resistencia de compresión de contenido vertical a 1000.

  2. Baje la prioridad de la altura de la restricción haciendo clic en "Editar" en Restricciones. Solo hazlo menos de 1000.

En Attributes Inspector:

  1. Desmarque "Desplazamiento habilitado"

Coloque UILabel oculto debajo de su vista de texto. Label lines = 0. Establezca las restricciones de UITextView para que sean iguales a UILabel (centerX, centerY, width, height). Funciona incluso si deja el comportamiento de desplazamiento de textView.


Descubrí que no es del todo infrecuente en situaciones en las que aún puede necesitar que isScrollEnabled se establezca en verdadero para permitir una interacción de IU razonable. Un caso simple para esto es cuando desea permitir una vista de texto que se expande automáticamente, pero aún limita su altura máxima a algo razonable en una UITableView.

Aquí hay una subclase de UITextView que se me ocurrió que permite la expansión automática con diseño automático, pero que aún se puede limitar a una altura máxima y que administrará si la vista es desplazable dependiendo de la altura. De forma predeterminada, la vista se expandirá indefinidamente si tiene configuradas las restricciones de esa manera.

import UIKit class FlexibleTextView: UITextView { // limit the height of expansion per intrinsicContentSize var maxHeight: CGFloat = 0.0 private let placeholderTextView: UITextView = { let tv = UITextView() tv.translatesAutoresizingMaskIntoConstraints = false tv.backgroundColor = .clear tv.isScrollEnabled = false tv.textColor = .disabledTextColor tv.isUserInteractionEnabled = false return tv }() var placeholder: String? { get { return placeholderTextView.text } set { placeholderTextView.text = newValue } } override init(frame: CGRect, textContainer: NSTextContainer?) { super.init(frame: frame, textContainer: textContainer) isScrollEnabled = false autoresizingMask = [.flexibleWidth, .flexibleHeight] NotificationCenter.default.addObserver(self, selector: #selector(UITextInputDelegate.textDidChange(_:)), name: Notification.Name.UITextViewTextDidChange, object: self) placeholderTextView.font = font addSubview(placeholderTextView) NSLayoutConstraint.activate([ placeholderTextView.leadingAnchor.constraint(equalTo: leadingAnchor), placeholderTextView.trailingAnchor.constraint(equalTo: trailingAnchor), placeholderTextView.topAnchor.constraint(equalTo: topAnchor), placeholderTextView.bottomAnchor.constraint(equalTo: bottomAnchor), ]) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var text: String! { didSet { invalidateIntrinsicContentSize() placeholderTextView.isHidden = !text.isEmpty } } override var font: UIFont? { didSet { placeholderTextView.font = font invalidateIntrinsicContentSize() } } override var contentInset: UIEdgeInsets { didSet { placeholderTextView.contentInset = contentInset } } override var intrinsicContentSize: CGSize { var size = super.intrinsicContentSize if size.height == UIViewNoIntrinsicMetric { // force layout layoutManager.glyphRange(for: textContainer) size.height = layoutManager.usedRect(for: textContainer).height + textContainerInset.top + textContainerInset.bottom } if maxHeight > 0.0 && size.height > maxHeight { size.height = maxHeight if !isScrollEnabled { isScrollEnabled = true } } else if isScrollEnabled { isScrollEnabled = false } return size } @objc private func textDidChange(_ note: Notification) { // needed incase isScrollEnabled is set to true which stops automatically calling invalidateIntrinsicContentSize() invalidateIntrinsicContentSize() placeholderTextView.isHidden = !text.isEmpty } }

Como beneficio adicional, se admite incluir texto de marcador similar a UILabel.


La respuesta de vitaminwater está funcionando para mí.

Si el texto de la vista de texto rebota hacia arriba y hacia abajo durante la edición, después de configurar [textView setScrollEnabled:NO]; , configure Size Inspector > Scroll View > Content Insets > Never .

Espero eso ayude.


Por cierto, construí una UITextView en expansión usando una subclase y anulando el tamaño del contenido intrínseco. Descubrí un error en UITextView que es posible que desee investigar en su propia implementación. Aquí está el problema:

La vista de texto en expansión aumentará para acomodar el texto en crecimiento si escribe letras individuales a la vez. Pero si pega un montón de texto en él, no crecería pero el texto se desplazaría hacia arriba y el texto en la parte superior quedaría fuera de la vista.

La solución: anule setBounds: en su subclase. Por algún motivo desconocido, el pegado causó que el valor de los límites.origin.y no fuera zee (33 en cada caso que vi). Así que anulé setBounds: para establecer siempre los boundaries.origin.y en cero. Solucionado el problema.


Puedes hacerlo a través del guión gráfico, solo deshabilita "Desplazamiento habilitado" :)


También puede hacerlo sin subclasificar UITextView . Echa un vistazo a mi respuesta a ¿Cómo dimensiono un UITextView a su contenido en iOS 7?

Usa el valor de esta expresión:

[textView sizeThatFits:CGSizeMake(textView.frame.size.width, CGFLOAT_MAX)].height

para actualizar la constant de altura del UILayoutConstraint .


UITextView no proporciona un tamaño de contenido intrínseco, por lo que debe subclasificarlo y proporcionarlo. Para que crezca automáticamente, invalide el intrinsicContentSize en layoutSubviews. Si usa algo que no sea el contentInset predeterminado (que no recomiendo), es posible que deba ajustar el cálculo intrinsicContentSize.

@interface AutoTextView : UITextView @end

#import "AutoTextView.h" @implementation AutoTextView - (void) layoutSubviews { [super layoutSubviews]; if (!CGSizeEqualToSize(self.bounds.size, [self intrinsicContentSize])) { [self invalidateIntrinsicContentSize]; } } - (CGSize)intrinsicContentSize { CGSize intrinsicContentSize = self.contentSize; // iOS 7.0+ if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f) { intrinsicContentSize.width += (self.textContainerInset.left + self.textContainerInset.right ) / 2.0f; intrinsicContentSize.height += (self.textContainerInset.top + self.textContainerInset.bottom) / 2.0f; } return intrinsicContentSize; } @end


Una cosa importante a tener en cuenta:

Como UITextView es una subclase de UIScrollView, está sujeta a la propiedad automaticallyAdjustsScrollViewInsets de UIViewController.

Si está configurando el diseño y TextView es la primera subvista en una jerarquía de UIViewControllers, tendrá sus contentInsets modificados si el ajuste automático de AdscrollViewInsets es verdadero, lo que a veces causa un comportamiento inesperado en el diseño automático.

Por lo tanto, si tiene problemas con el diseño automático y las vistas de texto, intente establecer automaticallyAdjustsScrollViewInsets = false en el controlador de vista o mover el avance de textView en la jerarquía.


A la vista que contiene UITextView se le asignará su tamaño con setBounds mediante setBounds . Entonces, esto es lo que hice. La supervista está configurada inicialmente para todas las otras restricciones como deberían ser, y al final puse una restricción especial para la altura de UITextView, y la guardé en una variable de instancia.

_descriptionHeightConstraint = [NSLayoutConstraint constraintWithItem:_descriptionTextView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:0.f constant:100]; [self addConstraint:_descriptionHeightConstraint];

En el método setBounds , cambié el valor de la constante.

-(void) setBounds:(CGRect)bounds { [super setBounds:bounds]; _descriptionTextView.frame = bounds; CGSize descriptionSize = _descriptionTextView.contentSize; [_descriptionHeightConstraint setConstant:descriptionSize.height]; [self layoutIfNeeded]; }


Obj C: #import <UIKit/UIKit.h> @interface ViewController : UIViewController @property (nonatomic) UITextView *textView; @end #import "ViewController.h" @interface ViewController () @end @implementation ViewController @synthesize textView; - (void)viewDidLoad{ [super viewDidLoad]; [self.view setBackgroundColor:[UIColor grayColor]]; self.textView = [[UITextView alloc] initWithFrame:CGRectMake(30,10,250,20)]; self.textView.delegate = self; [self.view addSubview:self.textView]; } - (void)didReceiveMemoryWarning{ [super didReceiveMemoryWarning]; } - (void)textViewDidChange:(UITextView *)txtView{ float height = txtView.contentSize.height; [UITextView beginAnimations:nil context:nil]; [UITextView setAnimationDuration:0.5]; CGRect frame = txtView.frame; frame.size.height = height + 10.0; //Give it some padding txtView.frame = frame; [UITextView commitAnimations]; } @end