So navigieren Sie durch Textfelder (Weiter / Fertig-Schaltflächen)

487

Wie kann ich mit der Schaltfläche "Weiter" auf der iPhone-Tastatur durch alle meine Textfelder navigieren?

Das letzte Textfeld sollte die Tastatur schließen.

Ich habe die IB the Buttons (Weiter / Fertig) eingerichtet, aber jetzt stecke ich fest.

Ich habe die Aktion textFieldShouldReturn implementiert, aber jetzt schließen die Schaltflächen Weiter und Fertig die Tastatur.

phx
quelle
Bitte werfen Sie einen Blick auf meine Antwort unten. Nach meiner Erfahrung ist es eine bessere, vollständigere Lösung als die normalerweise gegebene Antwort. Ich habe keine Ahnung, warum jemand eine negative Bewertung abgeben würde.
Memmons
Ich habe ein ähnliches Problem, aber mit Cordova, Phonegap. Gibt es Möglichkeiten, es zu lösen?

Antworten:

578

In Cocoa für Mac OS X haben Sie die nächste Responderkette, in der Sie das Textfeld fragen können, auf welches Steuerelement sich der Fokus als Nächstes konzentrieren soll. So funktioniert das Tabulieren zwischen Textfeldern. Da iOS-Geräte jedoch keine Tastatur und nur eine Berührung haben, hat dieses Konzept den Übergang zu Cocoa Touch nicht überstanden.

Dies kann ohnehin leicht gemacht werden, mit zwei Annahmen:

  1. Alles "tabbable" UITextField befinden sich in derselben übergeordneten Ansicht.
  2. Ihre "Tab-Reihenfolge" wird durch die Tag-Eigenschaft definiert.

Angenommen, Sie können textFieldShouldReturn überschreiben: wie folgt:

-(BOOL)textFieldShouldReturn:(UITextField*)textField
{
  NSInteger nextTag = textField.tag + 1;
  // Try to find next responder
  UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];
  if (nextResponder) {
    // Found next responder, so set it.
    [nextResponder becomeFirstResponder];
  } else {
    // Not found, so remove keyboard.
    [textField resignFirstResponder];
  }
  return NO; // We do not want UITextField to insert line-breaks.
}

Fügen Sie etwas mehr Code hinzu, und die Annahmen können ebenfalls ignoriert werden.

Swift 4.0

 func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    let nextTag = textField.tag + 1
    // Try to find next responder
    let nextResponder = textField.superview?.viewWithTag(nextTag) as UIResponder!

    if nextResponder != nil {
        // Found next responder, so set it
        nextResponder?.becomeFirstResponder()
    } else {
        // Not found, so remove keyboard
        textField.resignFirstResponder()
    }

    return false
}

Wenn die Übersicht des Textfelds eine UITableViewCell ist, ist dies der nächste Antwortende

let nextResponder = textField.superview?.superview?.superview?.viewWithTag(nextTag) as UIResponder!
PeyloW
quelle
6
Manchmal befinden sich oben auf der Tastatur die Schaltflächen "Weiter" und "Zurück". Zumindest im Browser.
Tim Büthe
31
Um pedantisch zu sein, möchte ich einige Fehlinformationen in diesem Beitrag beseitigen. Unter OS X wird die Tab-Reihenfolge über die setNextKeyView: (nicht setNextResponder :) -Methode von NSView eingerichtet. Die Responderkette ist davon getrennt: Es handelt sich um eine Hierarchie von Responderobjekten, die "nicht zielgerichtete" Aktionen verarbeiten, z. B. Aktionen, die an den ersten Responder anstatt direkt an ein Controller-Objekt gesendet werden. Die Responderkette folgt normalerweise der Ansichtshierarchie bis zum Fenster und seinem Controller und dann dem App-Delegaten. Google "Responder Chain" für viele Informationen.
Davehayden
Die Übersicht über das Textfeld ist a UITableViewCell. Sie müssten auf die zugehörige UITableViewZelle zugreifen und die Zelle mit dem gesuchten Textfeld abrufen.
Michael Mior
13
Vergessen Sie nicht, UITextFieldDelegatedie Textfelddelegierten hinzuzufügen und festzulegen.
Sal
1
Freundliche Erinnerung daran, die TAG-Option über das Storyboard festzulegen (in meinem Fall). Sie müssen das TextFields-Tag manuell in aufsteigender Reihenfolge festlegen. (Erstens ist 0, zweitens ist 1 usw.) Damit diese Lösung funktioniert.
Joakim
170

Es gibt eine viel elegantere Lösung, die mich beim ersten Mal umgehauen hat. Leistungen:

  • Näher an der Implementierung von OSX-Textfeldern, bei der ein Textfeld weiß, wo der Fokus als Nächstes liegen soll
  • Verlässt sich nicht auf das Setzen oder Verwenden von Tags - die für diesen Anwendungsfall IMO-fragil sind
  • Kann erweitert werden, um mit UITextFieldund zu arbeitenUITextView Steuerelementen zu arbeiten - oder mit jedem Steuerelement für die Benutzeroberfläche von Tastatureingaben
  • Überladen Sie Ihren View Controller nicht mit UITextField-Delegatencode auf der Boilerplate
  • Integriert sich gut in IB und kann über die bekannte Option per Drag & Drop konfiguriert werden, um Steckdosen anzuschließen.

Erstellen Sie eine UITextField-Unterklasse mit einer IBOutletEigenschaft namens nextField. Hier ist der Header:

@interface SOTextField : UITextField

@property (weak, nonatomic) IBOutlet UITextField *nextField; 

@end

Und hier ist die Implementierung:

@implementation SOTextField

@end

In Ihrem View Controller erstellen Sie die -textFieldShouldReturn:Delegate-Methode:

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    if ([textField isKindOfClass:[SOTextField class]]) {
        UITextField *nextField = [(SOTextField *)textField nextField];

        if (nextField) {
            dispatch_async(dispatch_get_current_queue(), ^{
                [nextField becomeFirstResponder];
            });
        }
        else {
            [textField resignFirstResponder];
        }
    }

    return YES;
}

Ändern Sie in IB Ihre UITextFields, um die SOTextFieldKlasse zu verwenden. Legen Sie als Nächstes auch in IB den Delegaten für jeden 'SOTextFields'to'-Dateibesitzer' fest (genau dort, wo Sie den Code für die Delegatenmethode eingeben - textFieldShouldReturn). Das Schöne an diesem Design ist, dass Sie jetzt einfach mit der rechten Maustaste auf ein beliebiges Textfeld klicken und dem nächsten SOTextFieldObjekt, für das Sie der nächste Antwortende sein möchten , den nextField-Ausgang zuweisen können .

NextField in IB zuweisen

Darüber hinaus können Sie coole Dinge tun, z. B. die textFields schleifen, sodass der erste wieder den Fokus erhält, nachdem der letzte den Fokus verloren hat.

Dies kann leicht erweitert werden , um automatisch das zuweisen returnKeyTypeder SOTextFieldzu einem , UIReturnKeyNextwenn es eine Nextfield zugeordnet ist - eine Sache weniger, manuell konfiguriert.

memmons
quelle
3
Ich mag die IB-Integration über die Verwendung von Tags - insbesondere mit den neueren Versionen von Xcode, die IB gut in den Rest der IDE integrieren - und es ist ein Bonus, meinen Controller nicht mit Text auf dem Boilerplate überladen zu müssen.
Memmons
4
Tags sind einfacher, aber dieser Weg ist besser und besser für ein gutes OO-Design geeignet
Brain2000
1
Diese Lösung gefällt mir sehr gut, aber ich verwende jedes Textfeld in separaten Zellenansichten in einem UITableView. Obwohl ich IBOutletauf dem Ansichtscontroller Eigenschaften für jedes Textfeld habe, kann ich die nextFieldEigenschaft nicht mit diesen Eigenschaften im Eigentümer der Datei verknüpfen . Irgendwelche Ratschläge, wie ich es in diesem Szenario zum Laufen bringen kann?
Jay Imerman
1
Mit @JayImerman IB können Sie keine Steckdosen zwischen Zellen verbinden. IB wird für dieses Szenario also ein No-Go sein. Sie können die nextField-Eigenschaften jedoch einfach mit Code verbinden. textField1.nextField = textField2; textField2.nextField = textField3;usw.
Memmons
2
Gibt es eine Chance, dass diese Antwort für neuere Versionen von xcode und ios aktualisiert wird?
Verlorene Übersetzung
82

Hier ist eine ohne Delegation:

tf1.addTarget(tf2, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)
tf2.addTarget(tf3, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)

Objekt:

[tf1 addTarget:tf2 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
[tf2 addTarget:tf3 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];

Funktioniert mit der (meist unbekannten) UIControlEventEditingDidEndOnExit UITextFieldAktion.

Sie können dies auch einfach in das Storyboard einbinden, sodass keine Delegierung oder Code erforderlich ist.

Bearbeiten: Eigentlich kann ich nicht herausfinden, wie ich das in das Storyboard einbinden kann. becomeFirstResponderscheint keine angebotene Aktion für dieses Kontrollereignis zu sein, was schade ist. Trotzdem können Sie alle Ihre Textfelder mit einer einzigen Aktion in Ihrem ViewController verknüpfen, die dann becomeFirstResponderbasierend auf dem Absender bestimmt, welches Textfeld verwendet werden soll (obwohl es dann nicht so elegant ist wie die obige programmatische Lösung, also macht IMO es mit dem obigen Code in viewDidLoad).

mxcl
quelle
18
Sehr einfache und effektive Lösung. Der letzte in der Kette kann sogar z [tfN addTarget:self action:@selector(loginButtonTapped:) forControlEvents:UIControlEventEditingDidEndOnExit];. Danke @mxcl.
Msrdjan
Wie würden Sie dies im Storyboard tun?
Pedro Borges
Dies ist wahrscheinlich die beste Antwort hier.
Daspianist
Dies hat auch in Swift 2 perfekt funktioniert. Dies ist eine viel einfachere und schnellere Lösung als das Hinzufügen von Delegierten.
Tonne
2
Swift 4: txtFirstname.addTarget (txtLastname, Aktion: #selector (werdenFirstResponder), für: UIControlEvents.editingDidEndOnExit)
realtimez
78

Hier ist meine Lösung für dieses Problem.

Um dies zu lösen (und weil ich es hasse, mich auf Tags zu verlassen), habe ich beschlossen, dem UITextField-Objekt eine benutzerdefinierte Eigenschaft hinzuzufügen. Mit anderen Worten, ich habe eine Kategorie auf UITextField wie folgt erstellt:

UITextField + Extended.h

@interface UITextField (Extended)

@property(retain, nonatomic)UITextField* nextTextField;

@end

UITextField + Extended.m

#import "UITextField+Extended.h"
#import <objc/runtime.h>

static char defaultHashKey;

@implementation UITextField (Extended)

- (UITextField*) nextTextField { 
    return objc_getAssociatedObject(self, &defaultHashKey); 
}

- (void) setNextTextField:(UITextField *)nextTextField{
    objc_setAssociatedObject(self, &defaultHashKey, nextTextField, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
}

@end

Hier ist, wie ich es benutze:

UITextField *textField1 = ...init your textfield
UITextField *textField2 = ...init your textfield
UITextField *textField3 = ...init your textfield

textField1.nextTextField = textField2;
textField2.nextTextField = textField3;
textField3.nextTextField = nil;

Und implementieren Sie die textFieldShouldReturn-Methode:

- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {

    UITextField *next = theTextField.nextTextField;
    if (next) {
        [next becomeFirstResponder];
    } else {
        [theTextField resignFirstResponder];
    }

    return NO; 
}

Ich habe jetzt eine Art verknüpfte Liste von UITextField, von denen jeder weiß, wer der nächste in der Zeile ist.

Hoffe es wird helfen.

Anth0
quelle
11
Dies ist die beste Lösung und sollte als Antwort gewählt werden.
Giovanni
4
Diese Lösung funktionierte für mich. Ich habe nur das nextTextField und das IBOutlet geändert, damit ich die Verkettung in meinem Storyboard einrichten kann.
Zachzurn
Wenn ich Lieblingsantworten speichern könnte, wäre dies eine davon. Danke für die saubere Lösung!
Matt Becker
1
Beachten Sie, dass Sie diese Eigenschaften auch als IBOutlets festlegen können, wenn IB Ihr Ding ist. . Seien Sie vorsichtig mit Namenskollisionen - Apple kann dies eventuell zu UIResponder hinzufügen.
Jasper Blues
Ist diese Lösung mit dem Interface Builder kompatibel? Ich meine, kann es IBOutlets und Storyboard-Referenzen verwenden, anstatt das nextTextField programmgesteuert festzulegen? IB scheint es Ihnen nicht zu erlauben, die UITextField-Klasse auf eine Kategorie festzulegen.
Pedro Borges
46

Eine schnelle Erweiterung, die die Antwort von mxcl anwendet, um dies besonders einfach zu machen (angepasst an Swift 2.3 von Traveller):

extension UITextField {
    class func connectFields(fields:[UITextField]) -> Void {
        guard let last = fields.last else {
            return
        }
        for i in 0 ..< fields.count - 1 {
            fields[i].returnKeyType = .Next
            fields[i].addTarget(fields[i+1], action: "becomeFirstResponder", forControlEvents: .EditingDidEndOnExit)
        }
        last.returnKeyType = .Done
        last.addTarget(last, action: #selector(UIResponder.resignFirstResponder), forControlEvents: .EditingDidEndOnExit)
    }
}

Es ist einfach zu bedienen:

UITextField.connectFields([field1, field2, field3])

Die Erweiterung setzt die Eingabetaste für alle außer dem letzten Feld auf "Weiter" und für das letzte Feld auf "Fertig" und verschiebt den Fokus / schließt die Tastatur, wenn diese getippt werden.

Schnell <2.3

extension UITextField {
    class func connectFields(fields:[UITextField]) -> Void {
        guard let last = fields.last else {
            return
        }
        for var i = 0; i < fields.count - 1; i += 1 {
            fields[i].returnKeyType = .Next
            fields[i].addTarget(fields[i+1], action: "becomeFirstResponder", forControlEvents: .EditingDidEndOnExit)
        }
        last.returnKeyType = .Done
        last.addTarget(last, action: "resignFirstResponder", forControlEvents: .EditingDidEndOnExit)
    }
}

SWIFT 3: Verwenden Sie so -

UITextField.connectFields(fields: [field1, field2])

Extension:
    extension UITextField {
        class func connectFields(fields:[UITextField]) -> Void {
            guard let last = fields.last else {
                return
            }
            for i in 0 ..< fields.count - 1 {
                fields[i].returnKeyType = .next
                fields[i].addTarget(fields[i+1], action: #selector(UIResponder.becomeFirstResponder), for: .editingDidEndOnExit)
            }
            last.returnKeyType = .go
            last.addTarget(last, action: #selector(UIResponder.resignFirstResponder), for: .editingDidEndOnExit)
        }
    }
Amos Joshua
quelle
5
Übrigens, wenn Sie möchten, dass Fertig eine Aktion ausführt, können Sie Ihren View Controller einfach anpassen UITextFieldDelegateund implementieren lassen func textFieldDidEndEditing(textField: UITextField). Kommen Sie einfach früh raus, wenn textField != field3.
Ryan
25

Eine konsistentere und robustere Methode ist die Verwendung von NextResponderTextField. Sie können es vollständig über den Interface Builder konfigurieren, ohne den Delegaten festlegen oder verwenden zu müssen view.tag.

Alles was Sie tun müssen, ist

  1. Stellen Sie den Klassentyp Ihres UITextFieldeinNextResponderTextField Geben Sie hier die Bildbeschreibung ein
  2. nextResponderFieldStellen Sie dann den Ausgang des to so ein, dass er auf den nächsten Responder zeigt. Es kann sich um eine UITextFieldbeliebige UIResponderUnterklasse handeln. Es kann auch ein UIButton sein, und die Bibliothek ist intelligent genug, um das TouchUpInsideEreignis der Schaltfläche nur dann auszulösen, wenn sie aktiviert ist. Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein

Hier ist die Bibliothek in Aktion:

Geben Sie hier die Bildbeschreibung ein

mohamede1945
quelle
Ja. Funktioniert sehr gut mit Swift 3.1, einer sehr eleganten Lösung, genau wie IB sofort funktionieren sollte.
Richard
Entschuldigung, ich habe die Frage nicht verstanden.
mohamede1945
1
NextResponderTextField hat für mich gearbeitet. Es war schnell und einfach zu bedienen, nur eine schnelle Datei, und tat genau das, was ich brauchte, ohne viel Aufhebens.
Pat McG
Das funktioniert super! Hinweis: Ich musste die Eingabetaste manuell ändern, damit "Weiter" und "Fertig" wie im Beispiel angezeigt werden.
Mike Miller
14

Ich mag die OO-Lösungen, die Anth0 und Answerbot bereits vorgeschlagen haben. Ich arbeitete jedoch an einem schnellen und kleinen POC, sodass ich die Dinge nicht mit Unterklassen und Kategorien überladen wollte.

Eine andere einfache Lösung besteht darin, ein NSArray von Feldern zu erstellen und das nächste Feld nachzuschlagen, wenn Sie auf Weiter klicken. Keine OO-Lösung, aber schnell, einfach und leicht zu implementieren. Außerdem können Sie die Reihenfolge auf einen Blick sehen und ändern.

Hier ist mein Code (basierend auf anderen Antworten in diesem Thread):

@property (nonatomic) NSArray *fieldArray;

- (void)viewDidLoad {
    [super viewDidLoad];

    fieldArray = [NSArray arrayWithObjects: firstField, secondField, thirdField, nil];
}

- (BOOL) textFieldShouldReturn:(UITextField *) textField {
    BOOL didResign = [textField resignFirstResponder];
    if (!didResign) return NO;

    NSUInteger index = [self.fieldArray indexOfObject:textField];
    if (index == NSNotFound || index + 1 == fieldArray.count) return NO;

    id nextField = [fieldArray objectAtIndex:index + 1];
    activeField = nextField;
    [nextField becomeFirstResponder];

    return NO;
}
  • Ich gebe immer NEIN zurück, weil ich keinen Zeilenumbruch einfügen möchte. Ich dachte nur, ich würde darauf hinweisen, da bei der Rückgabe von YES die nachfolgenden Felder automatisch verlassen oder ein Zeilenumbruch in meine Textansicht eingefügt werden. Ich habe ein bisschen Zeit gebraucht, um das herauszufinden.
  • activeField verfolgt das aktive Feld, falls ein Bildlauf erforderlich ist, um das Feld von der Tastatur freizugeben. Wenn Sie einen ähnlichen Code haben, stellen Sie sicher, dass Sie das aktive Feld zuweisen, bevor Sie den Ersthelfer ändern. Das Ändern des Ersthelfers erfolgt sofort und löst das KeyboardWasShown-Ereignis sofort aus.
Festzelt
quelle
Nett! Einfach und der Code ist alles in einem Modell. AnthO's ist auch sehr schön.
Seamus
Beachten Sie, dass Sie mit diesem Code das Risiko eingehen, Zyklen beizubehalten. Ihr fieldArray konvertiert die in all Ihren IBOutlets verwendeten schwachen Referenzen in starke Referenzen im Array selbst.
Michael Long
11

Hier ist eine Implementierung des Tabulierens mithilfe einer Kategorie in UIControl. Diese Lösung bietet alle Vorteile der Methoden von Michael und Anth0, funktioniert jedoch für alle UIControls, nicht nur für UITextFields. Es funktioniert auch nahtlos mit Interface Builder und Storyboards.

Quell- und Beispiel-App: GitHub-Repository für UIControlsWithTabbing

Verwendungszweck:

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    [textField transferFirstResponderToNextControl];
    return NO;
}

NextControl im Interface Builder zuweisen

Header:

//
// UIControl+NextControl.h
// UIControlsWithTabbing
//

#import <UIKit/UIKit.h>

@interface UIControl (NextControl)

@property (nonatomic, weak) IBOutlet UIControl *nextControl;

- (BOOL)transferFirstResponderToNextControl;

@end

Implementierung:

#import "UIControl+NextControl.h"
#import <objc/runtime.h>

static char defaultHashKey;

@implementation UIControl (NextControl)

- (UIControl *)nextControl
{
    return objc_getAssociatedObject(self, &defaultHashKey);
}

- (void)setNextControl:(UIControl *)nextControl
{
    objc_setAssociatedObject(self, &defaultHashKey, nextControl, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)transferFirstResponderToNextControl
{
    if (self.nextControl)
    {
        [self.nextControl becomeFirstResponder];

        return YES;
    }

    [self resignFirstResponder];

    return NO;
}

@end
Picciano
quelle
1
Manchmal ist das nächste Widget kein Steuerelement, dh UITextView, und Sie möchten möglicherweise auch dort eine Registerkarte erstellen. Ändern Sie den Typ von UITextField in UIResponder.
Pedro Borges
1
Funktioniert super und mit fantastischer Dokumentation. Ich wünschte, ich würde vor einigen Stunden in diese Frage scrollen. :)
Javier Cadiz
10

Ich habe viele Codes ausprobiert und schließlich hat dies in Swift 3.0 Latest [März 2017] für mich funktioniert.

Die ViewControllerKlasse sollte geerbt werden, UITextFieldDelegatedamit dieser Code funktioniert.

class ViewController: UIViewController,UITextFieldDelegate  

Fügen Sie das Textfeld mit der richtigen Tag-Nummer hinzu, und diese Tag-Nummer wird verwendet, um das Steuerelement basierend auf der ihm zugewiesenen inkrementellen Tag-Nummer zu einem geeigneten Textfeld zu führen.

override func viewDidLoad() {
    userNameTextField.delegate = self
    userNameTextField.tag = 0
    userNameTextField.returnKeyType = UIReturnKeyType.next
    passwordTextField.delegate = self
    passwordTextField.tag = 1
    passwordTextField.returnKeyType = UIReturnKeyType.go
}

Im obigen Code returnKeyType = UIReturnKeyType.nextwird die Rückgabetaste des Tastenfelds angezeigt, da NextSie auch andere Optionen wie Join/Gousw. haben. Abhängig von Ihrer Anwendung ändern Sie die Werte.

Dies textFieldShouldReturnist eine Methode, die von UITextFieldDelegate gesteuert wird. Hier haben wir die nächste Feldauswahl basierend auf der Inkrementierung des Tag-Werts

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
    if let nextField = textField.superview?.viewWithTag(textField.tag + 1) as? UITextField {
        nextField.becomeFirstResponder()
    } else {
        textField.resignFirstResponder()
        return true;
    }
    return false
 }
BHUVANESH MOHANKUMAR
quelle
Dies funktioniert, außer wenn das Eingabefeld ist UITextView. Irgendeine Idee, wie man das in Kombination macht?
T9b
@ T9b Was genau brauchst du? Welche Typsteuerung verwenden Sie in Ihrer App?
BHUVANESH MOHANKUMAR
Es gibt zwei Arten von Texteingaben: Die erste ist UITextField(einzelne Textzeile eingeben) und die andere UITextView(mehrere Textzeilen eingeben). UITextViewreagiert offensichtlich nicht auf diesen Code, kann aber in Formularen verwendet werden.
T9b
Ja, dieser Teil des Codes nur für UITextField. Haben Sie versucht, das Ereignis für UITextView zu ändern?
BHUVANESH MOHANKUMAR
9

Nachdem Sie ein Textfeld verlassen haben, rufen Sie [otherTextField werden zum ersten Antwortgeber] auf und das nächste Feld wird fokussiert.

Dies kann tatsächlich ein schwieriges Problem sein, da Sie häufig auch den Bildschirm scrollen oder die Position des Textfelds auf andere Weise anpassen möchten, damit es beim Bearbeiten leicht erkennbar ist. Stellen Sie einfach sicher, dass Sie viele Tests durchführen, indem Sie auf unterschiedliche Weise in die Textfelder ein- und ausgehen und auch frühzeitig abreisen (geben Sie dem Benutzer immer die Möglichkeit, die Tastatur zu schließen, anstatt zum nächsten Feld zu wechseln, normalerweise mit "Fertig" die Navigationsleiste)

Kendall Helmstetter Gelner
quelle
9
 -(BOOL)textFieldShouldReturn:(UITextField *)textField
{
   [[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
   return YES;
}
iKushal
quelle
Sie sollten zu diesem Zweck keine Tags verwenden.
Christian Schnorr
7

Eine sehr einfache Methode zum Schließen der Tastatur, wenn die Taste "Fertig" gedrückt wird, ist:

Erstellen Sie eine neue IBAction im Header

- (IBAction)textFieldDoneEditing:(id)sender;

Fügen Sie in der Implementierungsdatei (.m-Datei) die folgende Methode hinzu:

- (IBAction)textFieldDoneEditing:(id)sender 
{ 
  [sender resignFirstResponder];
}

Wenn Sie dann die IBAction mit dem Textfeld verknüpfen, verknüpfen Sie sie mit dem Ereignis "Did End On Exit".

jcrowson
quelle
1
Gern geschehen, es navigiert nicht durch die Textfelder, aber es schließt die Tastatur und ermöglicht Ihnen, ein anderes Feld auszuwählen.
JCrowson
7

Stellen Sie zuerst die Tastatur-Eingabetaste in xib ein, andernfalls können Sie Code schreiben in viewdidload:

passWord.returnKeyType = UIReturnKeyNext;

-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
    if(textField == eMail) {
        [textField resignFirstResponder];
        [userName becomeFirstResponder];
    }
    if (textField==userName) {
        [textField resignFirstResponder];
        [passWord becomeFirstResponder];
    }
    if (textField==passWord) {
        [textField resignFirstResponder];
        [country becomeFirstResponder];
    }
    if (textField==country) {
        [textField resignFirstResponder];
    }
    return YES;
}
Mahesh Chowdary
quelle
7

Ich bin überrascht, wie viele Antworten hier ein einfaches Konzept nicht verstehen: Das Navigieren durch Steuerelemente in Ihrer App sollte nicht von den Ansichten selbst ausgeführt werden. Es ist die Aufgabe des Controllers , zu entscheiden, welche Steuerung der nächste Ersthelfer sein soll.

Außerdem galten die meisten Antworten nur für das Vorwärtsnavigieren, aber Benutzer möchten möglicherweise auch rückwärts gehen.

Also hier ist, was ich mir ausgedacht habe. Ihr Formular sollte von einem View Controller verwaltet werden, und View Controller sind Teil der Responderkette. Sie können also die folgenden Methoden implementieren:

#pragma mark - Key Commands

- (NSArray *)keyCommands
{
    static NSArray *commands;

    static dispatch_once_t once;
    dispatch_once(&once, ^{
        UIKeyCommand *const forward = [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:0 action:@selector(tabForward:)];
        UIKeyCommand *const backward = [UIKeyCommand keyCommandWithInput:@"\t" modifierFlags:UIKeyModifierShift action:@selector(tabBackward:)];

        commands = @[forward, backward];
    });

    return commands;
}

- (void)tabForward:(UIKeyCommand *)command
{
    NSArray *const controls = self.controls;
    UIResponder *firstResponder = nil;

    for (UIResponder *const responder in controls) {
        if (firstResponder != nil && responder.canBecomeFirstResponder) {
            [responder becomeFirstResponder]; return;
        }
        else if (responder.isFirstResponder) {
            firstResponder = responder;
        }
    }

    [controls.firstObject becomeFirstResponder];
}

- (void)tabBackward:(UIKeyCommand *)command
{
    NSArray *const controls = self.controls;
    UIResponder *firstResponder = nil;

    for (UIResponder *const responder in controls.reverseObjectEnumerator) {
        if (firstResponder != nil && responder.canBecomeFirstResponder) {
            [responder becomeFirstResponder]; return;
        }
        else if (responder.isFirstResponder) {
            firstResponder = responder;
        }
    }

    [controls.lastObject becomeFirstResponder];
}

Möglicherweise gilt eine zusätzliche Logik zum Scrollen von zuvor sichtbaren Offscreen-Respondern.

Ein weiterer Vorteil dieses Ansatzes ist , dass Sie nicht alle Arten von Kontrollen , Unterklasse müssen Sie auf Display (wie möchten UITextFields) , sondern können stattdessen die Logik auf Controller - Ebene verwalten, wo, ehrlich lassen, ist der richtige Ort , dies zu tun, .

Christian Schnorr
quelle
Ich finde Ihre Antwort am relevantesten, aber ich habe etwas Lustiges bemerkt, das Sie vielleicht klären können. Wenn Sie UITextFields verwenden, die nicht von einem UIViewController verwaltet werden, wird durch Drücken der Tabulatortaste das nächste UITextField standardmäßig zum Ersthelfer. Wenn jedoch jeder von einem VC verwaltet wird, ist dies nicht der Fall. Meine VCs machen nichts, aber die eingebaute Tabbing-Funktionalität ist weg. Ich habe festgestellt, dass keyCommands des VC der Responderkette der Ansicht hinzugefügt werden. Dies lässt mich glauben, dass bei Verwendung eines VC die Implementierung von keyCommands obligatorisch ist.
Joey Carson
4

Ich habe die Antwort von PeyloW hinzugefügt, falls Sie eine Funktion für vorherige / nächste Schaltflächen implementieren möchten:

- (IBAction)moveThroughTextFields:(UIBarButtonItem *)sender 
{
    NSInteger nextTag;
    UITextView *currentTextField = [self.view findFirstResponderAndReturn];

    if (currentTextField != nil) {
        // I assigned tags to the buttons.  0 represent prev & 1 represents next
        if (sender.tag == 0) {
            nextTag = currentTextField.tag - 1;

        } else if (sender.tag == 1) {
            nextTag = currentTextField.tag + 1;
        }
    }
    // Try to find next responder
    UIResponder* nextResponder = [self.view viewWithTag:nextTag];
    if (nextResponder) {
        // Found next responder, so set it.
        // I added the resign here in case there's different keyboards in place.
        [currentTextField resignFirstResponder];
        [nextResponder becomeFirstResponder];
    } else {
        // Not found, so remove keyboard.
        [currentTextField resignFirstResponder];

    }
}

Wo Sie die UIView wie folgt unterordnen:

@implementation UIView (FindAndReturnFirstResponder)
- (UITextView *)findFirstResponderAndReturn
{
    for (UITextView *subView in self.subviews) {
        if (subView.isFirstResponder){
            return subView;
        }
    }
    return nil;
}
@end
James Mertz
quelle
4

Hallo an alle, bitte sehen Sie diesen

- (void)nextPrevious:(id)sender
{

  UIView *responder = [self.view findFirstResponder];   

  if (nil == responder || ![responder isKindOfClass:[GroupTextField class]]) {
    return;
  }

  switch([(UISegmentedControl *)sender selectedSegmentIndex]) {
    case 0:
      // previous
      if (nil != ((GroupTextField *)responder).previousControl) {
        [((GroupTextField *)responder).previousControl becomeFirstResponder];
        DebugLog(@"currentControl: %i previousControl: %i",((GroupTextField *)responder).tag,((GroupTextField *)responder).previousControl.tag);
      }
      break;
    case 1:
      // next
      if (nil != ((GroupTextField *)responder).nextControl) {
        [((GroupTextField *)responder).nextControl becomeFirstResponder];
        DebugLog(@"currentControl: %i nextControl: %i",((GroupTextField *)responder).tag,((GroupTextField *)responder).nextControl.tag);
      }     
      break;    
  }
}
Rithik
quelle
Anstelle von UIView * Responder wäre es genauer, UIRespoder * Responder zu verwenden
Pedro Borges
4

Ich habe versucht, dieses Problem mithilfe eines differenzierteren Ansatzes zu lösen, der darauf basiert, jeder Zelle (oder UITextField) UITableVieweinen eindeutigen Tag-Wert zuzuweisen, der später abgerufen werden kann: zuzuweisen activate-next-uitextfield-in-uitableview-ios

Ich hoffe das hilft!

Fabiano Francesconi
quelle
4

Ich habe gerade einen neuen Pod erstellt, wenn ich mich mit diesem Zeug beschäftige . GNTextFieldsCollectionManager . Es behandelt automatisch das nächste / letzte textField-Problem und ist sehr einfach zu bedienen:

[[GNTextFieldsCollectionManager alloc] initWithView:self.view];

Erfasst alle Textfelder, die nach Ansicht in der Ansichtshierarchie (oder nach Tags) sortiert sind, oder Sie können Ihr eigenes Array von Textfeldern angeben.

JakubKnejzlik
quelle
4

Lösung in Swift 3.1: Nachdem Sie Ihre Textfelder verbunden haben, legen IBOutlets Ihren Textfelddelegierten in viewDidLoad fest und navigieren dann in textFieldShouldReturn zu Ihrer Aktion

class YourViewController: UIViewController,UITextFieldDelegate {

        @IBOutlet weak var passwordTextField: UITextField!
        @IBOutlet weak var phoneTextField: UITextField!

        override func viewDidLoad() {
            super.viewDidLoad()
            self.passwordTextField.delegate = self
            self.phoneTextField.delegate = self
            // Set your return type
            self.phoneTextField.returnKeyType = .next
            self.passwordTextField.returnKeyType = .done
        }

        func textFieldShouldReturn(_ textField: UITextField) -> Bool{
            if textField == self.phoneTextField {
                self.passwordTextField.becomeFirstResponder()
            }else if textField == self.passwordTextField{
                // Call login api
                self.login()
            }
            return true
        }

    }
Krishna Thakur
quelle
4

Wenn jemand so will. Ich denke, dies kommt den in Frage gestellten Anforderungen am nächsten

Geben Sie hier die Bildbeschreibung ein

Hier ist, wie ich diesen implementiert habe

Fügen Sie für jedes Textfeld, für das Sie das Setup durchführen möchten, die Zusatzansicht hinzu

func setAccessoryViewFor(textField : UITextField)    {
    let toolBar = UIToolbar()
    toolBar.barStyle = .default
    toolBar.isTranslucent = true
    toolBar.sizeToFit()

    // Adds the buttons

    // Add previousButton
    let prevButton = UIBarButtonItem(title: "<", style: .plain, target: self, action: #selector(previousPressed(sender:)))
    prevButton.tag = textField.tag
    if getPreviousResponderFor(tag: textField.tag) == nil {
        prevButton.isEnabled = false
    }

    // Add nextButton
    let nextButton = UIBarButtonItem(title: ">", style: .plain, target: self, action: #selector(nextPressed(sender:)))
    nextButton.tag = textField.tag
    if getNextResponderFor(tag: textField.tag) == nil {
        nextButton.title = "Done"
    }

    let spaceButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
    toolBar.setItems([prevButton,spaceButton,nextButton], animated: false)
    toolBar.isUserInteractionEnabled = true
    textField.inputAccessoryView = toolBar
}

Verwenden Sie die folgenden Funktionen, um mit Wasserhähnen umzugehen

func nextPressed(sender : UIBarButtonItem) {
    if let nextResponder = getNextResponderFor(tag: sender.tag) {
        nextResponder.becomeFirstResponder()
    } else {
        self.view.endEditing(true)
    }

}

func previousPressed(sender : UIBarButtonItem) {
    if let previousResponder = getPreviousResponderFor(tag : sender.tag)  {
        previousResponder.becomeFirstResponder()
    }
}

func getNextResponderFor(tag : Int) -> UITextField? {
    return self.view.viewWithTag(tag + 1) as? UITextField
}

func getPreviousResponderFor(tag : Int) -> UITextField? {
    return self.view.viewWithTag(tag - 1) as? UITextField
}

Sie müssen die textFields-Tags in der Reihenfolge angeben, in der die Schaltfläche next / prev antworten soll.

Mohammad Sadiq
quelle
3

Ich bevorzuge lieber:

@interface MyViewController : UIViewController
@property (nonatomic, retain) IBOutletCollection(UIView) NSArray *inputFields;
@end

In der NIB-Datei hänge ich die textFields in der gewünschten Reihenfolge in dieses inputFields-Array ein. Danach mache ich einen einfachen Test für den Index des UITextField, der meldet, dass der Benutzer auf return getippt hat:

// for UITextField
-(BOOL)textFieldShouldReturn:(UITextField*)textField {
    NSUInteger index = [_inputFields indexOfObject:textField];
    index++;
    if (index < _inputFields.count) {
        UIView *v = [_inputFields objectAtIndex:index];
        [v becomeFirstResponder];
    }
    return NO;
}

// for UITextView
-(BOOL)textView:(UITextView*)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString*)text {
    if ([@"\n" isEqualToString:text]) {
        NSUInteger index = [_inputFields indexOfObject:textView];
        index++;
        if (index < _inputFields.count) {
            UIView *v = [_inputFields objectAtIndex:index];
            [v becomeFirstResponder];
        } else {
            [self.view endEditing:YES];
        }
        return NO;
    }
    return YES;
}
Anticro
quelle
3
if (cell == nil)
{
    cell = [[UITableViewCell-Zuweisung] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: cellIdentifier];
    txt_Input = [[UITextField alloc] initWithFrame: CGRectMake (0, 10, 150, 30)];
    txt_Input.tag = indexPath.row + 1;
    [self.array_Textfields addObject: txt_Input]; // Veränderbares Array in ViewDidLoad initialisieren
}}

- (BOOL) textFieldShouldReturn: (UITextField *) textField
{

    int tag = (int) textField.tag;
    UITextField * txt = [self.array_Textfields objectAtIndex: tag];
    [txt werden zum ersten Antwortgeber];
    JA zurückgeben;
}}
Rebecca Curties
quelle
3

Ich hatte ungefähr 10+ UITextField in meinem Storyboard und die Art und Weise, wie ich die nächste Funktionalität aktivierte, bestand darin, ein Array von UITextField zu erstellen und das nächste UITextField zum ersten Antwortenden zu machen. Hier ist die Implementierungsdatei:

#import "RegistrationTableViewController.h"

@interface RegistrationTableViewController ()
@property (weak, nonatomic) IBOutlet UITextField *fullNameTextField;
@property (weak, nonatomic) IBOutlet UITextField *addressTextField;
@property (weak, nonatomic) IBOutlet UITextField *address2TextField;
@property (weak, nonatomic) IBOutlet UITextField *cityTextField;
@property (weak, nonatomic) IBOutlet UITextField *zipCodeTextField;
@property (weak, nonatomic) IBOutlet UITextField *urlTextField;
@property (weak, nonatomic) IBOutlet UITextField *usernameTextField;
@property (weak, nonatomic) IBOutlet UITextField *emailTextField;
@property (weak, nonatomic) IBOutlet UITextField *passwordTextField;
@property (weak, nonatomic) IBOutlet UITextField *confirmPWTextField;

@end
NSArray *uiTextFieldArray;
@implementation RegistrationTableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"view did load");
    uiTextFieldArray = @[self.fullNameTextField,self.addressTextField,self.address2TextField,self.cityTextField,self.zipCodeTextField,self.urlTextField,self.usernameTextField,self.emailTextField,self.passwordTextField,self.confirmPWTextField];
    for(UITextField *myField in uiTextFieldArray){
        myField.delegate = self;
    }


}
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
    long index = [uiTextFieldArray indexOfObject:textField];
    NSLog(@"%ld",index);
    if(index < (uiTextFieldArray.count - 1)){
        [uiTextFieldArray[++index] becomeFirstResponder];
    }else{
        [uiTextFieldArray[index] resignFirstResponder];
    }
    return YES;
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end
Illegales Argument
quelle
3

Dies funktionierte bei mir in Xamarin.iOS / Monotouch. Ändern Sie die Tastaturschaltfläche in Weiter, übergeben Sie das Steuerelement an das nächste UITextField und blenden Sie die Tastatur nach dem letzten UITextField aus.

private void SetShouldReturnDelegates(IEnumerable<UIView> subViewsToScout )
{
  foreach (var item in subViewsToScout.Where(item => item.GetType() == typeof (UITextField)))
  {
    (item as UITextField).ReturnKeyType = UIReturnKeyType.Next;
    (item as UITextField).ShouldReturn += (textField) =>
    {
        nint nextTag = textField.Tag + 1;
        var nextResponder = textField.Superview.ViewWithTag(nextTag);
        if (null != nextResponder)
            nextResponder.BecomeFirstResponder();
        else
            textField.Superview.EndEditing(true); 
            //You could also use textField.ResignFirstResponder(); 

        return false; // We do not want UITextField to insert line-breaks.
    };
  }
}

In ViewDidLoad haben Sie:

Wenn Ihre TextFields kein Tag haben, legen Sie es jetzt fest:

txtField1.Tag = 0;
txtField2.Tag = 1;
txtField3.Tag = 2;
//...

und nur der Anruf

SetShouldReturnDelegates(yourViewWithTxtFields.Subviews.ToList());
//If you are not sure of which view contains your fields you can also call it in a safer way:
SetShouldReturnDelegates(txtField1.Superview.Subviews.ToList());
//You can also reuse the same method with different containerViews in case your UITextField are under different views.
Daniele D.
quelle
3

Dies ist eine einfache Lösung in Kürze, ohne Tag, ohne Storyboard-Tricks ...

Verwenden Sie einfach diese Erweiterung:

extension UITextField{

    func nextTextFieldField() -> UITextField?{
        //field to return
        var returnField : UITextField?
        if self.superview != nil{
            //for each view in superview
            for (_, view) in self.superview!.subviews.enumerate(){
                //if subview is a text's field
                if view.isKindOfClass(UITextField){
                    //cast curent view as text field
                    let currentTextField = view as! UITextField
                    //if text field is after the current one
                    if currentTextField.frame.origin.y > self.frame.origin.y{
                        //if there is no text field to return already
                        if returnField == nil {
                            //set as default return
                            returnField = currentTextField
                        }
                            //else if this this less far than the other
                        else if currentTextField.frame.origin.y < returnField!.frame.origin.y{
                            //this is the field to return
                            returnField = currentTextField
                        }
                    }
                }
            }
        }
        //end of the mdethod
        return returnField
    }

}

Und nennen Sie es (zum Beispiel) mit Ihrem Textfelddelegierten so:

func textFieldShouldReturn(textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    textField.nextTextFieldField()?.becomeFirstResponder()
    return true
}
Kevin ABRIOUX
quelle
Ein Kommentar dazu ist, dass Ihre Methode davon ausgeht, dass alle Felder vertikal in einer einzigen Übersicht gestapelt sind. Es erlaubt keine gruppierten Felder und funktioniert nicht, wenn Textfelder nebeneinander liegen. Letzteres ist heutzutage besonders wichtig, wenn man Größenklassen verwendet, um Formulare für horizontale Ansichten neu anzuordnen, und wenn Formulare auf Tablets angezeigt werden.
Michael Long
3

Ein sicherer und direkterer Weg, vorausgesetzt:

  • Die Textfelddelegierten werden auf Ihren Ansichtscontroller festgelegt
  • Alle Textfelder sind Unteransichten derselben Ansicht
  • Die Textfelder haben Tags in der Reihenfolge, in der Sie fortfahren möchten (z. B. textField2.tag = 2, textField3.tag = 3 usw.).
  • Wenn Sie auf die Schaltfläche " Zurück" auf der Tastatur tippen, können Sie zum nächsten Textfeld wechseln (Sie können dies auf " Weiter" , " Fertig" usw. ändern .)
  • Sie möchten, dass die Tastatur nach dem letzten Textfeld geschlossen wird

Swift 4.1:

extension ViewController: UITextFieldDelegate {
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        let nextTag = textField.tag + 1
        guard let nextTextField = textField.superview?.viewWithTag(nextTag) else {
            textField.resignFirstResponder()
            return false
        }

    nextTextField.becomeFirstResponder()

    return false

    }
}
Jason z
quelle
2

In textFieldShouldReturn sollten Sie überprüfen, ob das Textfeld, in dem Sie sich gerade befinden, nicht das letzte ist, wenn Sie auf Weiter klicken und wenn die Tastatur nicht geschlossen wird.

Daniel
quelle
Hey, ok das funktioniert, aber wie kann ich mit der Schaltfläche "Weiter" zum nächsten Textfeld springen?
Phx
2

Dies ist ein alter Beitrag, der jedoch einen hohen Seitenrang hat, sodass ich mich meiner Lösung anschließen werde.

Ich hatte ein ähnliches Problem und erstellte schließlich eine Unterklasse von UIToolbar, um die nächste / vorherige / erledigte Funktionalität in einer dynamischen Tabellenansicht mit folgenden Abschnitten zu verwalten: https://github.com/jday001/DataEntryToolbar

Sie legen die Symbolleiste als inputAccessoryView Ihrer Textfelder fest und fügen sie dem Wörterbuch hinzu. Auf diese Weise können Sie sie auch bei dynamischen Inhalten vorwärts und rückwärts durchlaufen. Es gibt Delegate-Methoden, wenn Sie Ihre eigene Funktionalität auslösen möchten, wenn die textField-Navigation stattfindet, Sie sich jedoch nicht mit der Verwaltung von Tags oder dem Status eines Ersthelfers befassen müssen.

Unter dem GitHub-Link befinden sich Codefragmente und eine Beispiel-App, die bei den Implementierungsdetails helfen. Sie benötigen ein eigenes Datenmodell, um die Werte in den Feldern zu verfolgen.

jday
quelle
2

Ohne Verwendung von Tags und ohne Hinzufügen einer Eigenschaft für nextField / nextTextField können Sie versuchen, TAB zu emulieren, wobei "testInput" Ihr aktuell aktives Feld ist:

if ([textInput isFirstResponder])
    [textInput.superview.subviews enumerateObjectsAtIndexes:
     [NSIndexSet indexSetWithIndexesInRange:
      NSMakeRange([textInput.superview.subviews indexOfObject:textInput]+1,
                  [textInput.superview.subviews count]-[textInput.superview.subviews indexOfObject:textInput]-1)]
                                                    options:0 usingBlock:^(UIView *obj, NSUInteger idx, BOOL *stop) {
                                                        *stop = !obj.hidden && [obj becomeFirstResponder];
                                                    }];
if ([textInput isFirstResponder])
    [textInput.superview.subviews enumerateObjectsAtIndexes:
     [NSIndexSet indexSetWithIndexesInRange:
      NSMakeRange(0,
                  [textInput.superview.subviews indexOfObject:textInput])]
                                                    options:0 usingBlock:^(UIView *obj, NSUInteger idx, BOOL *stop) {
                                                        *stop = !obj.hidden && [obj becomeFirstResponder];
                                                    }];
Cœur
quelle
2

Ich benutze die Antwort von Michael G. Emmons seit ungefähr einem Jahr und funktioniert großartig. Ich habe kürzlich bemerkt, dass das Aufrufen von resignFirstResponder und sofortigem Werden von FirstResponder dazu führen kann, dass die Tastatur "fehlerhaft" wird, verschwindet und sofort angezeigt wird. Ich habe seine Version leicht geändert, um den resignFirstResponder zu überspringen, wenn das nächste Feld verfügbar ist.

- (BOOL) textFieldShouldReturn: (UITextField *) textField
{ 

    if ([textField isKindOfClass: [NRTextField class]])
    {
        NRTextField * nText = (NRTextField *) textField;
        if ([nText nextField]! = nil) {
            dispatch_async (dispatch_get_main_queue (),
                           ^ {[[nText nextField] werdenFirstResponder]; });

        }}
        sonst{
            [textField resignFirstResponder];
        }}
    }}
    sonst{
        [textField resignFirstResponder];
    }}

    return true;

}}
Arinmorf
quelle