Cambiar el tamaño de un UITextField mientras se escribe (mediante el uso de Autodiseño)


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

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

Nada elegante, y funciona como se espera.

introduzca la descripción de la imagen aquí

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

No me gusta eso, el ancho del campo de texto debe ajustarse mientras el usuario escribe en ese campo de texto.

En mi opinión, eso debería funcionar fuera de la caja. Implementando un IBAction para el evento UIControlEventEditingChanged puedo confirmar que escribir realmente cambia el intrinsicContentSize del campo Uitext.

Pero bueno, el ancho no cambia hasta que el campo de texto ya no es el primer respondedor. Si pongo 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 intentar hacer que esto funcione?

Author: Matthias Bauch, 2013-08-14

9 answers

Esto es lo que funciona para mí:

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

Lo interesante es que parece que debería funcionar fuera 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 de contenido intrínseco, la vista debe llamar invalidateIntrinsicContentSize para que el sistema de diseño observe la cambiar y puede volver a diseñar. En la implementación de su clase view, debe asegurarse de que si el valor de cualquier propiedad sobre la cual el el tamaño intrínseco depende de los cambios, que invoca Invalidateintrinsicontentsize. Por ejemplo, un campo de texto llama invalidateIntrinsicContentSize si cambia el valor de la cadena.

Mi mejor conjetura es que textfield solo llama a invalidateIntrinsicContentSize una vez que la edición ha terminado, no durante.

Editar: Un montón de "Esto no funciona para mí". Creo que la confusión aquí es tal vez el evento desencadenante que está vinculado a la textFieldDidChange: handler. El evento necesita ser UIControlEventEditingChanged. Si está utilizando IB, compruebe que está manejando el evento correcto.

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

 53
Author: TomSwift,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-21 18:29:40

No se por qué UITextField solo actualiza su intrinsicContentSize cuando renuncia a su estado de primer respondedor. Esto sucede tanto para iOS 7 como para iOS 8.

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

// 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í lo hago más amplio para dar cuenta del caret

 17
Author: onmyway133,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-27 08:21:03

Aquí está la respuesta en Swift. Como beneficio adicional, este fragmento no colapsa 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()
    }
}
 4
Author: WaltersGE1,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-22 19:16:33

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

Lo que hice fue algo similar a esto: {[10]]}

Considerando self.contentView es el 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 de mis UITextView's superview.

Si eliges optar por esa solución (tal vez por un enfoque más afinado), puedes 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 se mencionó, la opción anular fue utilizado por mí porque necesitaba un enfoque más afinado.

Además, como siempre, tendrá que asegurarse de que está comprobando los casos extremos (en términos de valores de tamaño) que cree que podría encontrar.

Espero que esto ayude!

Salud!

 1
Author: codeBearer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-16 19:17:10

First things first: en Swift 4, el 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íxeles. El textfield se asigna a la clase CurrencyTextField que hereda de UITextField . CurrencyTextField se extiende para proporcionar un comportamiento personalizado para devolver el objeto intrinsicContentSize, una variable calculada. .
El evento" editing changed " de la instancia CurrencyTextField se asigna al método repositionAndResize IBAction en su clase host UIViewController. Este método simplemente invalida el tamaño de contenido intrínseco actual de la instancia de CurrencyTextField. Invocar el método invalidateIntrinsicContentSize de CurrencyTextField (forma heredada UITextField ) desencadena un intento de obtener el valor intrínseco del objeto tamaño del contenido.

La lógica personalizada en el método intrinsicContentSize getter de CurrencyTextField tiene que convertir la propiedad text de CurrentTextField a NSString para determinar su tamaño, que devuelve como intrinsicContentSize si la coordenada x del 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()

    }

}
 1
Author: sigmonky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-21 14:54:08

Otra solución para esto es establecer restricciones iniciales/finales en el campo de texto y usar isActive para activarlas/desactivarlas.

Al iniciar la edición, enciéndalos, si el texto está centrado, el efecto será el mismo (el texto parece redimensionarse automáticamente al escribir). Cuando se detenga la edición, desactive las restricciones, que envolverán el marco alrededor del texto.

 0
Author: bauerMusic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-19 08:07:52

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

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

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

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {

    textField.backgroundColor = [UIColor grayColor];
    return YES;
}
 0
Author: robegamesios,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-16 09:58:31

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

if ([self respondsToSelector:@selector(setEdgesForExtendedLayout:)])
{
    self.edgesForExtendedLayout = UIRectEdgeNone;
}
 0
Author: Eric Lee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-07 09:33:53

Por alguna razón, llamar a invalidateIntrinsicContentSize tampoco funcionó para mí, pero también me encontré con 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 hay que hacer es agregar una llamada a eso cuando se cambia el texto, como en las otras respuestas. Si eso no está funcionando, entonces aquí hay una solución (adaptada de aquí ) 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 está funcionando por sí mismo , esto terminará contando dos veces el cambio, por lo que primero debe verificarse.

 0
Author: Learn OpenGL ES,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:09:56