Usando “Siguiente” como clave de retorno

Utilizo el valor “Siguiente” para la “Tecla de retorno” para obtener el botón Siguiente en lugar del botón Listo, pero (obviamente) al presionarlo no se mueve automáticamente al siguiente cuadro UIText en mi vista.

¿Cuál es la forma correcta de hacer esto? He visto muchas respuestas, pero ¿alguien tiene una solución rápida?

Asegúrese de que sus campos de texto tengan su conjunto de delegates e implemente el método textFieldShouldReturn . Este es el método que se llama cuando el usuario toca la tecla de retorno (sin importar cómo se vea).

El método podría verse más o menos así:

 func textFieldShouldReturn(textField: UITextField) -> Bool { if textField == self.field1 { self.field2.becomeFirstResponder() } return true } 

La lógica actual aquí puede variar. Existen numerosos enfoques, y definitivamente desaconsejaría una cadena if / else masiva if tiene muchos campos de texto, pero lo esencial aquí es determinar qué vista está actualmente activa para determinar qué vista debería activarse. Una vez que haya determinado qué vista debe becomeFirstResponder , becomeFirstResponder método becomeFirstResponder esa vista.


Para cierta limpieza del código, puede considerar una extensión UITextField que se parece a esto:

 private var kAssociationKeyNextField: UInt8 = 0 extension UITextField { var nextField: UITextField? { get { return objc_getAssociatedObject(self, &kAssociationKeyNextField) as? UITextField } set(newField) { objc_setAssociatedObject(self, &kAssociationKeyNextField, newField, .OBJC_ASSOCIATION_RETAIN) } } } 

Y luego cambie nuestro método textFieldShouldReturn para que se vea así:

 func textFieldShouldReturn(textField: UITextField) -> Bool { textField.nextField?.becomeFirstResponder() return true } 

Una vez que haya hecho esto, debería simplemente ser una cuestión de establecer la nueva propiedad nextField cada campo de texto en viewDidLoad :

 self.field1.nextField = self.field2 self.field2.nextField = self.field3 self.field3.nextField = self.field4 self.field4.nextField = self.field1 

Aunque si realmente lo quisiéramos, podríamos prefijar la propiedad con @IBOutlet , y eso nos permitiría conectar nuestra propiedad “nextField” directamente en el constructor de interfaces.

Cambie la extensión para que se vea así:

 private var kAssociationKeyNextField: UInt8 = 0 extension UITextField { @IBOutlet var nextField: UITextField? { get { return objc_getAssociatedObject(self, &kAssociationKeyNextField) as? UITextField } set(newField) { objc_setAssociatedObject(self, &kAssociationKeyNextField, newField, .OBJC_ASSOCIATION_RETAIN) } } } 

Y ahora conecte la propiedad nextField en el constructor de interfaz:

enter image description here

(Configure su delegado mientras está aquí también).


Y, por supuesto, si la propiedad nextField devuelve nil , el teclado simplemente se esconde.

Aquí hay un ejemplo en Swift:

UITextField una pantalla con 6 UITextField s. Les asigné las tags 1 a 6 en Interface Builder. También cambié la tecla Volver a Siguiente en IB. Luego implementé lo siguiente:

 import UIKit // Make your ViewController a UITextFieldDelegate class ViewController: UIViewController, UITextFieldDelegate { // Use a dictionary to define text field order 1 goes to 2, 2 goes to 3, etc. let nextField = [1:2, 2:3, 3:4, 4:5, 5:6, 6:1] override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. // Make ourselves the delegate of the text fields so that textFieldShouldReturn // will be called when the user hits the Next/Return key for i in 1...6 { if let textField = self.view.viewWithTag(i) as? UITextField { textField.delegate = self } } } // This is called when the user hits the Next/Return key func textFieldShouldReturn(textField: UITextField) -> Bool { // Consult our dictionary to find the next field if let nextTag = nextField[textField.tag] { if let nextResponder = textField.superview?.viewWithTag(nextTag) { // Have the next field become the first responder nextResponder.becomeFirstResponder() } } // Return false here to avoid Next/Return key doing anything return false } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } 

No hay nada de malo con las otras respuestas, este es solo un enfoque diferente con el beneficio de estar más enfocado en OOP – imho (aunque esto es un poco más de trabajo por adelantado, puede ser reutilizado). En el guión gráfico, comienzo agregando tags con un rango distinto (por ejemplo, 800-810) que definen el orden específico de los campos en los que quiero moverme. Esto tiene la ventaja de trabajar en todas las subvistas en la vista principal para que pueda navegar entre UITextField y UITextView (y cualquier otro control) según sea necesario.

En general, normalmente bash tener mensajes de controladores de vista entre las vistas y los objetos personalizados del controlador de eventos. Así que utilizo un mensaje (también conocido como NSNotification) devuelto al controlador de vista desde una clase de delegado personalizada.

(Manejador de delegates TextField)

Nota: En AppDelegate.swift: let defaultCenter = NSNotificationCenter.defaultCenter ()

 //Globally scoped struct MNGTextFieldEvents { static let NextButtonTappedForTextField = "MNGTextFieldHandler.NextButtonTappedForTextField" } class MNGTextFieldHandler: NSObject, UITextFieldDelegate { var fields:[UITextField]? = [] func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool { return true } func textFieldDidBeginEditing(textField: UITextField) { textField.backgroundColor = UIColor.yellowColor() } func textFieldDidEndEditing(textField: UITextField) { textField.backgroundColor = UIColor.whiteColor() } func textFieldShouldBeginEditing(textField: UITextField) -> Bool { return true } func textFieldShouldClear(textField: UITextField) -> Bool { return false } func textFieldShouldEndEditing(textField: UITextField) -> Bool { return true } func textFieldShouldReturn(textField: UITextField) -> Bool { //passes the message and the textField (with tag) calling the method defaultCenter.postNotification(NSNotification(name: MNGTextFieldEvents.NextButtonTappedForTextField, object: textField)) return false } } 

Esto permite que mi controlador de vista permanezca enfocado en su trabajo principal de manejar los mensajes entre los objetos, el modelo y la vista.

(El controlador de View recibe un mensaje del delegado y pasa instrucciones usando la función advanceToNextField)

Nota: En mi guión gráfico, mis clases de controlador personalizado se definen usando un NSObject y ese objeto está vinculado al guión gráfico como un delegado para los controles que necesito monitorear. Lo que hace que la clase de controlador personalizado se inicialice automáticamente.

 class MyViewController: UIViewController { @IBOutlet weak var tagsField: UITextField! { didSet { (tagsField.delegate as? MNGTextFieldHandler)!.fields?.append(tagsField) } } @IBOutlet weak var titleField: UITextField!{ didSet { (titleField.delegate as? MNGTextFieldHandler)!.fields?.append(titleField) } } @IBOutlet weak var textView: UITextView! { didSet { (textView.delegate as? MNGTextViewHandler)!.fields?.append(textView) } } private struct Constants { static let SelectorAdvanceToNextField = Selector("advanceToNextField:") } override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) registerEventObservers() } override func viewDidDisappear(animated: Bool) { super.viewDidDisappear(animated) deRegisterEventObservers() } func advanceToNextField(notification:NSNotification) { let currentTag = (notification.object as! UIView).tag for aView in self.view.subviews { if aView.tag == currentTag + 1 { aView.becomeFirstResponder() } } } func registerEventObservers () { defaultCenter.addObserver(self, selector: Constants.SelectorAdvanceToNextField, name: MNGTextFieldEvents.NextButtonTappedForTextField, object: nil) } func deRegisterEventObservers() { defaultCenter.removeObserver(self, name: MNGTextFieldEvents.NextButtonTappedForTextField, object: nil) } .... } 

Solo otra forma de lograr el resultado que encontré útil. Mi aplicación tenía 11 campos de texto seguidos de una vista de texto. Necesitaba poder recorrer todos los campos usando la siguiente tecla y luego renunciar al teclado siguiendo la vista de texto (es decir, otras notas).

En el guión gráfico, configuré la etiqueta en todos los campos (texto y vista de texto) comenzando con 1 a 12, siendo 12 la vista de texto.

Estoy seguro de que hay otras formas de hacerlo y este método no es perfecto, pero espero que ayude a alguien.

En el código, escribí lo siguiente:

 func textFieldShouldReturn(textField: UITextField) -> Bool { let nextTag = textField.tag + 1 //Handle Textview transition, Textfield programmatically if textField.tag == 11 { //Current tag is 11, next field is a textview self.OtherNotes.becomeFirstResponder() } else if nextTag > 11 { //12 is the end, close keyboard textField.resignFirstResponder() } else { //Between 1 and 11 cycle through using next button let nextResponder = self.view.viewWithTag(nextTag) as? UITextField nextResponder?.becomeFirstResponder() } return false } func textFieldDidEndEditing(textField: UITextField) { textField.resignFirstResponder() } func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool { //Remove keyboard when clicking Done on keyboard if(text == "\n") { textView.resignFirstResponder() return false } return true }