¿Cómo verifico cuando un UITextField cambia?

290

Estoy tratando de verificar cuándo cambia un campo de texto, equivalente también a la función utilizada para textView; textViewDidChangehasta ahora he hecho esto:

  func textFieldDidBeginEditing(textField: UITextField) {
        if self.status.text == "" && self.username.text == "" {
            self.topRightButton.enabled = false
        } else {   
            self.topRightButton.enabled = true
        }
    }

¿Qué tipo de trabajo funciona, pero topRightButtonse habilita tan pronto como se presiona el campo de texto, quiero que se habilite solo cuando el texto se ingrese?

boraseoksoon
fuente

Respuestas:

739

RÁPIDO

Swift 4.2

textfield.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), for: .editingChanged)

y

@objc func textFieldDidChange(_ textField: UITextField) {

}

SWIFT 3 y swift 4.1

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), for: .editingChanged)

y

func textFieldDidChange(_ textField: UITextField) {

}

SWIFT 2.2

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)), forControlEvents: UIControlEvents.EditingChanged)

y

func textFieldDidChange(textField: UITextField) {
    //your code
}

C OBJETIVO

[textField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged];

y el método textFieldDidChange es

-(void)textFieldDidChange :(UITextField *) textField{
    //your code
}
Fawad Masud
fuente
Esto se bloquea para mí y no entiendo por qué.
Levi Roberts
1
Comprobado varias veces. El delegado se establece inmediatamente antes de entrar viewDidLoad. La acción es letra por letra igual. La aplicación se bloquea tan pronto como se presiona un botón del teclado. Editar: ¡Lo descubrí! Faltaba el punto y coma dentro de la acción. Supuse que solo tenía que ser el mismo que el nombre de la función.
Levi Roberts
@FawadMasud esto no hace nada ahora en Swift 2.0 en iOS 9 con XCode 7 ¿se ha depreciado o sabes la forma actual de solucionarlo?
Cody Weaver
1
@bibscy sí, tienes que recorrer todos los campos de texto dentro de una vista.
Fawad Masud
1
Para Swift 4.2 es: Texttfield.addTarget (self, action: #selector (ViewControllerr.textFieldDidChange (_ :)), para: UIControl.Event.editingChanged)
Exitare el
128

Puede hacer esta conexión en el generador de interfaces.

  1. En su guión gráfico, haga clic en el editor asistente en la parte superior de la pantalla (dos círculos en el medio). Asistente de editor seleccionado

  2. Ctrl + clic en el campo de texto en el generador de interfaces.

  3. Arrastre desde EditingChanged al interior de su clase de controlador de vista en la vista de asistente. Haciendo conexión

  4. Asigne un nombre a su función ("textDidChange", por ejemplo) y haga clic en conectar. Función de nomenclatura

rmooney
fuente
3
Esta es una gran solución, especialmente si se trata de un UITextField en una tableViewCell que está siendo administrada por una fuente de datos separada. Este enfoque permite que viewController responda directamente (por lo tanto, el origen de datos no tiene que responder y delegar la acción).
wuf810
1
Genial: una solución simple para un problema irritante. Por supuesto, puede vincular varios campos de texto
Jeremy Andrews, el
1
Probablemente una mejor respuesta que la anterior porque elimina la suma @objc func.
Matthew Bradshaw
Buena idea, uso el evento DidEndEditing
Puji Wahono el
Esta es la mejor solución. Gracias @rmooney!
Jonathan
63

Swift 5.0

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)),
                          for: .editingChanged)

y método de manejo:

@objc func textFieldDidChange(_ textField: UITextField) {

}

Swift 4.0

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)),
                          for: UIControlEvents.editingChanged)

y método de manejo:

@objc func textFieldDidChange(_ textField: UITextField) {

}

Swift 3.0

textField.addTarget(self, action: #selector(textFieldDidChange(textField:)), for: .editingChanged)

y método de manejo:

func textFieldDidChange(textField: UITextField) { 

}
Robert
fuente
29

La forma en que lo he manejado hasta ahora: en UITextFieldDelegate

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool
{
    // text hasn't changed yet, you have to compute the text AFTER the edit yourself
    let updatedString = (textField.text as NSString?)?.stringByReplacingCharactersInRange(range, withString: string)

    // do whatever you need with this updated string (your code)


    // always return true so that changes propagate
    return true
}

Versión Swift4

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    let updatedString = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)
    return true
}
Vinzzz
fuente
1
Esto no se llamará cuando un campo de texto esté vacío y el usuario haga clic en retroceso.
Matthew Mitchell el
14

Swift 3

 textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(sender:)), for: UIControlEvents.editingChanged)
Alessandro Mattiuzzi
fuente
7

Swift 3.0.1+ (Algunas de las otras respuestas de swift 3.0 no están actualizadas)

textField.addTarget(self, action: #selector(ViewController.textFieldDidChange(_:)),
                          for: UIControlEvents.editingChanged)

func textFieldDidChange(_ textField: UITextField) {

}
aviran
fuente
6

textField (_: shouldChangeCharactersIn: replaceString :) funcionó para mí en Xcode 8, Swift 3 si desea verificar cada pulsación de tecla.

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    // Whatever code you want to run here.
    // Keep in mind that the textfield hasn't yet been updated,
    // so use 'string' instead of 'textField.text' if you want to
    // access the string the textfield will have after a user presses a key

    var statusText = self.status.text
    var usernameText = self.username.text

    switch textField{
    case self.status:
        statusText = string
    case self.username:
        usernameText = string
    default:
        break
    }

    if statusText == "" && usernameText == "" {
        self.topRightButton.enabled = false
    } else {   
        self.topRightButton.enabled = true
    }

    //Return false if you don't want the textfield to be updated
    return true
}
radthemad4
fuente
5

Swift 4

Conforme a UITextFieldDelegate .

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    // figure out what the new string will be after the pending edit
    let updatedString = (textField.text as NSString?)?.replacingCharacters(in: range, with: string)

    // Do whatever you want here


    // Return true so that the change happens
    return true
}
Drewster
fuente
4

Puede usar este método delegado de UITextFieldDelegate. Se dispara con cada cambio de personaje.

(Objective C) textField:shouldChangeCharactersInRange:replacementString:
(Swift) textField(_:shouldChangeCharactersInRange:replacementString:)

Sin embargo, ESTO SÓLO DISPONE ANTES DE QUE SE HAGA UN CAMBIO (de hecho, solo se hace un cambio si devuelve verdadero desde aquí).

Abubakr Dar
fuente
1
¿Cómo debería escribirse esto, ya que también probé este método y llegué a la misma solución donde solo cambia una vez que se activa el textField, y no una vez que el texto realmente cambia?
Cuando implementa el método de delegado anterior, se activa cada vez que cambia su texto. Solo necesita agregar este código, self.textfield.delegate = self
Abubakr Dar
Para mí, este método no funcionó porque no se pudo verificar si el campo de texto estaba vacío dentro del método. Principalmente porque devuelve verdadero / falso dependiendo de SI el campo de texto puede cambiar. Entonces, el evento se dispara ANTES de que el campo de texto haya tenido la oportunidad de quedar vacío.
Levi Roberts
@LeviRoberts, tiene una referencia al campo de texto dentro de este método. Para que pueda verificar si el textfield.text está vacío.
Abubakr Dar
No pareces entenderlo. Cuando está vacío, el .isEmptymétodo no equivale a verdadero hasta DESPUÉS de que este método haya tenido la oportunidad de devolver verdadero; para decirle a la aplicación que el campo de texto debería cambiar.
Levi Roberts
3

¿Quizás usar RxSwift?

necesitar

pod 'RxSwift',    '~> 3.0'
pod 'RxCocoa',    '~> 3.0'

agregar importaciones obviamente

import RxSwift
import RxCocoa

Entonces tienes un textfield : UITextField

let observable: Observable<String?> = textField.rx.text.asObservable()
observable.subscribe(
            onNext: {(string: String?) in
                print(string!)
        })

U tiene otros 3 métodos.

  1. onError
  2. onCompleted
  3. onDisposed
  4. En el siguiente
marlonpya
fuente
Para recibir eventos de cambio real solamente y no también cuando el campo de texto se convirtió en el primer respondedor, debe usar distinciónUntilChanged en el texto.
RealNmae
1

Swift 4

textField.addTarget(self, action: #selector(textIsChanging), for: UIControlEvents.editingChanged)

@objc func textIsChanging(_ textField:UITextField) {

 print ("TextField is changing")

}

Si desea realizar un cambio una vez que el usuario ha ingresado completamente (se llamará una vez que el usuario cierre el teclado o presione Intro).

textField.addTarget(self, action: #selector(textDidChange), for: UIControlEvents.editingDidEnd)

 @objc func textDidChange(_ textField:UITextField) {

       print ("TextField did changed") 
 }
Anamika
fuente
1
txf_Subject.addTarget(self, action:#selector(didChangeFirstText), for: .editingChanged)

@objc func didChangeText(textField:UITextField) {
    let str = textField.text
    if(str?.contains(" "))!{
        let newstr = str?.replacingOccurrences(of: " ", with: "")
        textField.text = newstr
    }
}

@objc func didChangeFirstText(textField:UITextField) {
    if(textField.text == " "){
        textField.text = ""
    }
}
Yogesh Tandel
fuente
1

Debes seguir estos pasos:

  1. Hacer una referencia de salida al campo de texto
  2. AssignUITextFieldDelegate a la clase de controlador
  3. Configure yourTextField.delegate
  4. Implemente cualquier función que necesite

Código de muestra:

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var yourTextFiled : UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()

        yourTextFiled.delegate = self
    }


    func textFieldDidEndEditing(_ textField: UITextField) {
        // your code
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        // your code
    }

    .
    .
    .
}
M.Hazara
fuente
0

Así es como puede agregar un textField text change listeneruso de Swift 3 :

Declara tu clase como UITextFieldDelegate

override func viewDidLoad() {
    super.viewDidLoad()

    textField.delegate = self

    textField.addTarget(self, action: #selector(UITextFieldDelegate.textFieldShouldEndEditing(_:)), for: UIControlEvents.editingChanged)
}

Luego, tradicionalmente agregue una función textFieldShouldEndEditing:

func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { // do stuff
        return true 
}
Codetard
fuente
0

Swift 4.2

escribe esto en viewDidLoad

// to detect if TextField changed
TextField.addTarget(self, action: #selector(textFieldDidChange(_:)),
                                   for: UIControl.Event.editingChanged)

escribe esto fuera de viewDidLoad

@objc func textFieldDidChange(_ textField: UITextField) {
    // do something
}

Puede cambiar el evento mediante UIControl.Event.editingDidBegin o lo que quiera detectar.

genio
fuente
0

En caso de que esté interesado en una solución SwiftUI, esto está funcionando para mí:

 TextField("write your answer here...",
            text: Binding(
                     get: {
                        return self.query
                       },
                     set: { (newValue) in
                        self.fetch(query: newValue) // any action you need
                                return self.query = newValue
                      }
            )
  )

Tengo que decir que no es mi idea, lo leí en este blog: Enlace SwiftUI: un truco muy simple

abanet
fuente
0

En caso de que no sea posible enlazar addTarget a su UITextField, le aconsejo que enlace uno de ellos como se sugirió anteriormente e inserte el código para su ejecución al final del método shouldChangeCharactersIn.

nameTextField.addTarget(self, action: #selector(RegistrationViewController.textFieldDidChange(_:)), for: .editingChanged)

@objc func textFieldDidChange(_ textField: UITextField) {
    if phoneNumberTextField.text!.count == 17 && nameTextField.text!.count > 0 {
        continueButtonOutlet.backgroundColor = UIColor(.green)
    } else {
        continueButtonOutlet.backgroundColor = .systemGray
    }
}

Y en la llamada en shouldChangeCharactersIn func.

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    guard let text = textField.text else {
        return true
    }
    let lastText = (text as NSString).replacingCharacters(in: range, with: string) as String

    if phoneNumberTextField == textField {
        textField.text = lastText.format("+7(NNN)-NNN-NN-NN", oldString: text)
        textFieldDidChange(phoneNumberTextField)
        return false
    }
    return true
}
Степан Варткинаян
fuente
-1

rápido 4

En viewDidLoad ():

    //ADD BUTTON TO DISMISS KEYBOARD

    // Init a keyboard toolbar 
    let toolbar = UIView(frame: CGRect(x: 0, y: view.frame.size.height+44, width: view.frame.size.width, height: 44))
    toolbar.backgroundColor = UIColor.clear

    // Add done button
    let doneButt = UIButton(frame: CGRect(x: toolbar.frame.size.width - 60, y: 0, width: 44, height: 44))
    doneButt.setTitle("Done", for: .normal)
    doneButt.setTitleColor(MAIN_COLOR, for: .normal)
    doneButt.titleLabel?.font = UIFont(name: "Titillium-Semibold", size: 13)
    doneButt.addTarget(self, action: #selector(dismissKeyboard), for: .touchUpInside)
    toolbar.addSubview(doneButt)

    USDTextField.inputAccessoryView = toolbar

Agregue esta función:

    @objc func dismissKeyboard() {
      //Causes the view (or one of its embedded text fields) to resign the first responder status.
      view.endEditing(true)
    }
Nulo
fuente
-1

crear nueva clase personalizada MaterialTextfield.swift

class MaterialTextfield: UITextField,UITextFieldDelegate {

var bottomBorder = UIView()
var shouldShowEditing = false

override func awakeFromNib() {

    // Setup Bottom-Border

    self.delegate = self
    self.translatesAutoresizingMaskIntoConstraints = false

    bottomBorder = UIView.init(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
    bottomBorder.backgroundColor = UIColor(rgb: 0xE2DCD1) // Set Border-Color
    bottomBorder.translatesAutoresizingMaskIntoConstraints = false

    addSubview(bottomBorder)

    bottomBorder.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    bottomBorder.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
    bottomBorder.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
    bottomBorder.heightAnchor.constraint(equalToConstant: 1).isActive = true // Set Border-Strength

}
@IBInspectable var hasError: Bool = false {
    didSet {
        if (hasError) {
            bottomBorder.backgroundColor = UIColor.red//error color
        } else {
            bottomBorder.backgroundColor = UIColor(rgb: 0xE2DCD1)//passive color
        }

    }
}
@IBInspectable var showEditing: Bool = false{
    didSet {
        if (showEditing) {
            bottomBorder.backgroundColor = UIColor(rgb: 0x56B5CA)//active color
        } else {
            bottomBorder.backgroundColor = UIColor(rgb: 0xE2DCD1)//passive color
        }

    }

}

func textFieldDidBeginEditing(_ textField: UITextField) {//listen to on edit event
    showEditing = !self.showEditing
}
func textFieldDidEndEditing(_ textField: UITextField) {//listen to on end edit event
    showEditing = !self.showEditing
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {//listen to return button event
    textField.resignFirstResponder() // return button will close keyboard
    return true
}

}
Muhammad Asyraf
fuente
Con el debido respeto, esta es una solución horrible. Solo quiere verificar si a UITextFieldha actualizado su valor, ¿por qué crear una clase demasiado compleja para resolver este simple problema?
Guilherme Matuella
@GuilhermeMatuella esto es más para el código front end para que el usuario sepa si el campo es obligatorio y está lleno. Es un enfoque diferente para resolver el mismo problema. esto es básicamente mi activo personalizado
Muhammad Asyraf