2014-11-19 5 views
28

Ich benutze den "Next" -Wert für die "Return-Taste", um die Schaltfläche "Next" anstelle der Schaltfläche "Done" zu erhalten, aber (offensichtlich) drücken Sie nicht automatisch zum nächsten UITextField aus meiner Sicht.Verwenden von "Next" als Return-Taste

Was ist der richtige Weg? Ich habe viele Antworten gesehen, aber jeder hat eine schnelle Lösung?

Antwort

79

Stellen Sie sicher, dass in Ihren Textfeldern der Delegatsatz festgelegt ist, und implementieren Sie die Methode textFieldShouldReturn. Dies ist die Methode, die aufgerufen wird, wenn der Benutzer die Return-Taste (egal wie es aussieht) antippt.

Das Verfahren könnte wie folgt aussehen:

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

    return true 
} 

Die eigentliche Logik hier in kann variieren. Es gibt zahlreiche Ansätze, und ich würde auf jeden Fall von einer massiven Kette abraten, wenn Sie viele Textfelder haben, aber der Kern hier ist zu bestimmen, welche Ansicht gerade aktiv ist, um festzustellen, welche Ansicht aktiv werden soll. Sobald Sie bestimmt haben, welche Ansicht aktiv werden soll, rufen Sie die Methode becomeFirstResponder dieser Ansicht auf.


Für einige Code Sauberkeit, können Sie eine UITextField Erweiterung betrachten, die etwa wie folgt aussieht:

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

Once:

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) 
     } 
    } 
} 

wie folgt und dann unsere textFieldShouldReturn Methode ändern aussehen Wenn Sie dies getan haben, sollten Sie einfach die neue nextField-Eigenschaft jedes Textfelds in viewDidLoad festlegen :

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

Obwohl, wenn wir wirklich wollten wir das Grundstück mit @IBOutlet Präfix könnten, und das würde es uns ermöglichen, unser „Nextfield“ Eigentumsrecht in Interface Builder zu anschließen.

Ändern Sie die Erweiterung wie folgt aussehen:

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) 
     } 
    } 
} 

Und nun die nextField Eigenschaft in Interface Builder Haken:

enter image description here

(Richten Sie Ihren Delegierten, während Sie auch hier sind .)


Und von co Wenn die nextField -Eigenschaft nil zurückgibt, versteckt sich die Tastatur nur. Hier

+0

Wo ich die Erweiterung Code in der Datei stellen Sie? –

+0

Überall. Ich würde eine neue Datei dafür machen. – nhgrif

+0

Ich bekomme immer einen Fehler mit der "Erweiterung". Es besagt, dass diese Deklaration nur bei der Dateiquelle gültig ist. –

8

ist ein Beispiel in Swift:

ich einen Bildschirm mit 6 UITextField s erstellt. Ich habe ihnen die Tags 1 bis 6 im Interface Builder zugewiesen. Ich habe auch den Return-Schlüssel in IB in Next geändert.Dann implementiert ich folgendes:

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. 
    } 
} 
+0

Sollte wahrscheinlich funktionieren 'textField.superView? .viewWithTag (nextTag) als ? UITextField', und warum geben Sie 'false' von' textFieldShouldReturn' zurück? – nhgrif

+0

Wenn wir uns dafür einsetzen, in IB so genau zu sein, dass wir sehr spezifische Tags für jedes Textfeld setzen (was ich gegen diesen Wartungs-Albtraum empfehle), warum sollten wir dann nicht auch den Delegierten von textField in IB einrichten? – nhgrif

+0

Wie haben Sie jedem UITextField ein Tag zugewiesen? –

2

Es gibt nichts falsch mit den anderen Antworten ist, ist dies nur ein anderer Ansatz mit dem Vorteil, dass sie mehr auf OOP - imho (obwohl dies ein bisschen mehr Arbeit ist vorne, es kann wiederverwendet werden). Im Storyboard fange ich an, Tags mit einem bestimmten Bereich (z. B. 800-810) hinzuzufügen, die die spezifische Reihenfolge der Felder definieren, zwischen denen ich wechseln möchte. Dies hat den Vorteil, dass alle Teilansichten in der Hauptansicht bearbeitet werden, so dass man nach Bedarf zwischen UITextFields und UITextViews (und jedem anderen Steuerelement) navigieren kann.

Im Allgemeinen - ich versuche normalerweise, View-Controller-Nachrichten zwischen Ansichten und benutzerdefinierten Event-Handler-Objekten zu haben. Also verwende ich eine Nachricht (auch bekannt als NSNotification), die von einer benutzerdefinierten Delegate-Klasse an den View-Controller zurückgegeben wird.

(TextField- Delegierter Handler)

Hinweis: In AppDelegate.swift: lassen 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 
    } 
} 

Dies ermöglicht es meiner Ansicht Controller fokussiert bleiben auf sie Mains Aufgabe der Nachrichtenübermittlung zwischen Objekten, Modell und Ansicht.

(View-Controller erhält eine Nachricht von den Delegierten und übergibt Anweisungen, um die advanceToNextField Funktion)

Hinweis: In meinem Storyboard meiner benutzerdefinierten Handler-Klassen definiert werden, um eine NSObject verwenden und das Objekt wird in das Storyboard verbunden als ein Delegierter für die Kontrollen, die ich überwachen muss. Dadurch wird die benutzerdefinierte Handler-Klasse automatisch initialisiert.

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) 
    } 

    .... 
} 
1

Nur ein weiterer Weg, um das Ergebnis zu erzielen, die ich hilfreich. Meine App hatte 11 Textfelder gefolgt von einer Textansicht. Ich musste in der Lage sein, mit der nächsten Taste durch alle Felder zu radeln und dann nach der Textansicht (d. H. Anderen Notizen) die Tastatur zu verlassen.

Im Storyboard setze ich das Tag auf alle Felder (Text und Textansicht), beginnend mit 1 bis 12, wobei 12 die Textansicht ist.

Ich bin sicher, es gibt andere Möglichkeiten, es zu tun und diese Methode ist nicht perfekt, aber hoffentlich hilft es jemandem.

Im Code schrieb ich folgendes:

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 
}