iphone cocoa-touch uitextfield autolayout

iphone - Cambiar el tamaño de un UITextField mientras escribe(mediante el uso de Autolayout)



cocoa-touch (9)

Aquí está la respuesta en Swift. Como extra, este fragmento no colapsará el campo de texto si hay un marcador de posición.

// UITextField subclass override func awakeFromNib() { super.awakeFromNib() self.addTarget(self, action: #selector(textFieldTextDidChange), forControlEvents: .EditingChanged) } func textFieldTextDidChange(textField: UITextField) { textField.invalidateIntrinsicContentSize() } override func intrinsicContentSize() -> CGSize { if self.editing { let textSize: CGSize = NSString(string: ((text ?? "" == "") ? self.placeholder : self.text) ?? "").sizeWithAttributes(self.typingAttributes) return CGSize(width: textSize.width + (self.leftView?.bounds.size.width ?? 0) + (self.rightView?.bounds.size.width ?? 0) + 2, height: textSize.height) } else { return super.intrinsicContentSize() } }

Tengo una UITableViewCell que tiene dos UITextFields (sin bordes). Las siguientes restricciones se utilizan para configurar el diseño horizontal.

@"|-10-[leftTextField(>=80)]-(>=10)-[rightTextField(>=40)]-10-|"

Nada elegante, y funciona como se esperaba.

Como puede ver, el texto superior tiene el tamaño correcto. El textField inferior comenzó con un texto vacío, y debido al texto vacío tiene 80 puntos de ancho. Cuando escribo texto en el texto de edición Campo, el texto se desplaza hacia la izquierda, no cambia su ancho.

No me gusta eso, el ancho del textField debería ajustarse mientras el usuario escribe en ese campo de texto.

En mi opinión, eso debería funcionar de la caja. Al implementar un IBAction para el evento UIControlEventEditingChanged , puedo confirmar que el tipado realmente cambia el intrinsicContentSize del UITextField.

Pero bueno, el ancho no cambia hasta que textField ya no sea el primer respondedor. Si coloco el cursor en otro campo de texto, se establece el ancho del campo de texto editado. Eso es un poco tarde para lo que quiero.

Estas líneas comentadas es lo que intenté sin ningún éxito:

- (IBAction)textFieldDidChange:(UITextField *)textField { [UIView animateWithDuration:0.1 animations:^{ // [self.contentView removeConstraints:horizontalConstraints]; // [self.contentView addConstraints:horizontalConstraints]; // [self.contentView layoutIfNeeded]; // [self.contentView setNeedsLayout]; // [self.contentView setNeedsUpdateConstraints]; }]; NSLog(@"%@", NSStringFromCGSize(textField.intrinsicContentSize)); }

¿Alguien sabe lo que me estoy perdiendo? ¿Qué podría tratar de hacer que esto funcione?


Esto es lo que funciona para mí:

- (IBAction) textFieldDidChange: (UITextField*) textField { [UIView animateWithDuration:0.1 animations:^{ [textField invalidateIntrinsicContentSize]; }]; }

Lo que es interesante es que parece que DEBE funcionar de la caja, dado este texto de los documentos :

Además, si una propiedad de una vista cambia y ese cambio afecta el tamaño del contenido intrínseco, la vista debe llamar a invalidateIntrinsicContentSize para que el sistema de diseño note el cambio y pueda volver a diseñarlo. En la implementación de su clase de vista, debe asegurarse de que si el valor de cualquier propiedad del cual depende el tamaño intrínseco cambia, invoque invalidateIntrinsicContentSize. Por ejemplo, un campo de texto llama a invalidateIntrinsicContentSize si el valor de la cadena cambia.

Mi mejor suposición es que el campo de texto solo llama a invalidateIntrinsicContentSize una vez que la edición ha finalizado, no durante.

Editar: Un montón de "Esto no funciona para mí". Creo que la confusión aquí es quizás el evento desencadenante que está ligado al textFieldDidChange: handler. El evento debe ser UIControlEventEditingChanged. Si está utilizando IB, verifique que esté manejando el evento correcto.

El UITextField tampoco se puede restringir en tamaño. Puede bloquearlo en su posición con restricciones, pero cualquier restricción de ancho o un conjunto de restricciones de posicionamiento izquierda + derecha evitarán que cambie el tamaño de su tamaño de contenido intrínseco.


No sé por qué UITextField solo actualiza su intrinsicContentSize cuando renuncia a su estado de primera respuesta. Esto sucede tanto para iOS 7 como para iOS 8.

Como solución temporal, typingAttributes intrinsicContentSize y determino el tamaño usando typingAttributes . Esto también cuenta con leftView y rightView

// This method is target-action for UIControlEventEditingChanged func textFieldEditingChanged(textField: UITextField) { textField.invalidateIntrinsicContentSize() } override var intrinsicContentSize: CGSize { if isEditing { let string = (text ?? "") as NSString let size = string.size(attributes: typingAttributes) return CGSize(width: size.width + (rightView?.bounds.size.width ?? 0) + (leftView?.bounds.size.width ?? 0) + 2, height: size.height) } return super.intrinsicContentSize }

Aquí hago que sea más amplio para dar cuenta de la caret


Otra solución para esto es establecer restricciones de entrada / salida al textField y usar isActive para activarlas / desactivarlas.

Cuando edite el inicio, enciéndalos; si el texto está centrado, el efecto será el mismo (el texto parecerá tener un tamaño automático al escribir). Cuando la edición se detiene, desactiva las restricciones, que envolverán el marco alrededor del texto.


Para mí, también agregué un objetivo al campo de texto (UIControlEventEditingChanged)

- (void)textFieldChanged:(UITextField *)textField { [textField sizeToFit]; }

si tiene un color de fondo, simplemente actualícelo en el método de delegado:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { textField.backgroundColor = [UIColor grayColor]; return YES; }


Por alguna razón, llamar a invalidateIntrinsicContentSize tampoco funcionaba para mí, pero también tuve problemas con las otras soluciones debido a los márgenes y los controles de edición.

Esta solución no siempre es necesaria; si invalidateIntrinsicContentSize funciona, entonces todo lo que se necesita hacer es agregar una llamada cuando se cambie el texto, como en las otras respuestas. Si eso no funciona, entonces aquí hay una solución (adaptada de here ) que encontré que también funciona con un control claro:

class AutoResizingUITextField: UITextField { var lastTextBeforeEditing: String? override init(frame: CGRect) { super.init(frame: frame) setupTextChangeNotification() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setupTextChangeNotification() } func setupTextChangeNotification() { NotificationCenter.default.addObserver( forName: Notification.Name.UITextFieldTextDidChange, object: self, queue: OperationQueue.main) { (notification) in self.invalidateIntrinsicContentSize() } NotificationCenter.default.addObserver( forName: Notification.Name.UITextFieldTextDidBeginEditing, object: self, queue: OperationQueue.main) { (notification) in self.lastTextBeforeEditing = self.text } } override var intrinsicContentSize: CGSize { var size = super.intrinsicContentSize if isEditing, let text = text, let lastTextBeforeEditing = lastTextBeforeEditing { let string = text as NSString let stringSize = string.size(attributes: typingAttributes) let origSize = (lastTextBeforeEditing as NSString).size(attributes: typingAttributes) size.width = size.width + (stringSize.width - origSize.width) } return size } deinit { NotificationCenter.default.removeObserver(self) } }

Si invalidateIntrinsicContentSize funciona por sí solo , esto terminará contando dos veces el cambio, por lo que debe verificarse primero.


Puede agregar este código en viewDidLoad para resolver el problema.

if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)]) { self.edgesForExtendedLayout = UIRectEdgeNone; }


Tenía un requisito similar con una UITextView (tenía que aumentar dinámicamente su altura y algunas otras cosas).

Lo que hice fue algo similar a esto:

Teniendo en cuenta self.contentView es la superview de textField

- (IBAction)textFieldDidChange:(UITextField *)textField { //You can also use "textField.superview" instead of "self.contentView" [self.contentView setNeedsUpdateConstraints]; //Since you wish to animate that expansion right away... [UIView animateWithDuration:0.1 animations:^{ [self.contentView updateConstraintsIfNeeded]; }]; NSLog(@"%@", NSStringFromCGSize(textField.intrinsicContentSize)); }

Además, teniendo en cuenta los requisitos que tenía, tuve que anular el método updateConstraints la updateConstraints de mi superview .

Si elige optar por esa solución (tal vez por un enfoque más preciso), puede hacer:

- (void)updateConstraints { [super updateConstraints]; //Firstly, remove the width constraint from the textField. [self.myTextField removeConstraint:self.textFieldWidthConstraint]; self.textFieldWidthConstraint = nil; CGSize contentSize = self.myTextField.intrinsicContentSize; self.textFieldWidthConstraint = [NSLayoutConstraint constraintWithItem:self.myTextField attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1 constant:contentSize.width]; [self.myTextField addConstraint:self.textFieldWidthConstraint]; }

Como mencioné, utilicé la opción de anulación porque necesitaba un enfoque más preciso.

Además, como siempre, deberá asegurarse de que está buscando posibles casos límite (en términos de valores de tamaño) que cree que podría enfrentar.

¡Espero que esto ayude!

¡Aclamaciones!


Lo primero es lo primero : en Swift 4, intrinsicContentSize de UITextField es una variable calculada, no un método.

A continuación se muestra una solución Swift4 con un caso de uso especializado. Un campo de texto justificado a la derecha puede redimensionarse y extenderse hacia la izquierda hasta cierto límite de píxel. El campo de texto se asigna a la clase CurrencyTextField que hereda de UITextField . CurrencyTextField se amplía para proporcionar un comportamiento personalizado para devolver el intrinsicContentSize del objeto, una variable calculada. .
El evento " edición modificada " de la instancia de CurrencyTextField se asigna al método repositionAndResize IBAction en su clase host UIViewController. Este método simplemente invalida el tamaño del contenido intrínseco actual de la instancia de CurrencyTextField. Invocar el método invalidateIntrinsicContentSize de CurrencyTextField (formulario heredado UITextField) desencadena un intento de obtener el tamaño de contenido intrínseco del objeto.

La lógica personalizada en el método intrinsicContentSize getter de CurrencyTextField tiene que convertir la propiedad de texto de CurrentTextField en NSString para determinar su tamaño, que devuelve como el intrinsicContentSize si la coordenada x de CurrencyTextField es mayor que el límite de píxeles estipulado.

class CurrencyTextField:UITextField { } extension CurrencyTextField { override open var intrinsicContentSize: CGSize { if isEditing { let string = (text ?? "") as NSString let stringSize:CGSize = string.size(withAttributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 19.0)]) if self.frame.minX > 71.0 { return stringSize } } return super.intrinsicContentSize } } class TestController: UIViewController { @IBAction func repositionAndResize(_ sender: CurrencyTextField) { sender.invalidateIntrinsicContentSize() } override func viewDidLoad() { super.viewDidLoad() } }