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 die Buttons eingerichtet (Next/Done), aber jetzt stecke ich fest.
Ich habe die Aktion textFieldShouldReturn implementiert, jetzt schließen die Schaltflächen Weiter und Fertig die Tastatur.
In Cocoa für Mac OS X haben Sie die nächste Responder-Kette, in der Sie das Textfeld abfragen können, welches Steuerelement als Nächstes den Fokus haben soll. Dies bewirkt, dass das Tabgen zwischen Textfeldern funktioniert. Da iOS-Geräte jedoch keine Tastatur, sondern nur Berührung haben, hat dieses Konzept den Übergang zu Cocoa Touch nicht überstanden.
Dies kann mit zwei Annahmen trotzdem leicht gemacht werden:
UITextField
s befinden sich in derselben übergeordneten Ansicht.Angenommen, Sie können textFieldShouldReturn: wie folgt überschreiben:
-(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 weiteren 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 der Überblick über das Textfeld eine UITableViewCell sein wird, ist der nächste Antwortende
let nextResponder = textField.superview?.superview?.superview?.viewWithTag(nextTag) as UIResponder!
Es gibt eine viel elegantere Lösung, die mich beim ersten Mal umgehauen hat. Leistungen:
UITextField
- und UITextView
-Steuerelementen zu arbeiten - oder mit einem beliebigen UI-Steuerelement für die TastatureingabeErstellen Sie eine UITextField-Unterklasse, die über eine IBOutlet
-Eigenschaft namens nextField verfügt. Hier ist der Header:
@interface SOTextField : UITextField
@property (weak, nonatomic) IBOutlet UITextField *nextField;
@end
Und hier ist die Implementierung:
@implementation SOTextField
@end
In Ihrem Ansichtscontroller erstellen Sie die Delegatmethode -textFieldShouldReturn:
:
- (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 SOTextField
-Klasse zu verwenden. Als nächstes setzen Sie, ebenfalls in IB, den Delegaten für jeden der 'SOTextFields'to-Besitzer der Datei' (wo Sie den Code für die Delegatmethode - textFieldShouldReturn - angeben). Das Schöne an diesem Design ist, dass Sie jetzt einfach mit der rechten Maustaste auf ein beliebiges textField klicken und den nextField-Auslass dem nächsten SOTextField
-Objekt zuordnen können, das Sie als nächster Responder verwenden möchten.
Darüber hinaus können Sie coole Dinge wie das Schleifen der TextFields ausführen, so dass nach dem letzten Fokus der Fokus das erste wieder den Fokus erhält.
Dies kann leicht erweitert werden, um die returnKeyType
der SOTextField
automatisch einer UIReturnKeyNext
zuzuweisen, wenn ein nextField zugewiesen ist - eine Sache weniger manuell konfigurieren.
Hier ist meine Lösung für dieses Problem.
Um dieses Problem zu lösen (und weil ich es hasse, mich auf Tags zu verlassen, um Dinge zu tun), entschied ich mich, dem UITextField-Objekt eine benutzerdefinierte Eigenschaft hinzuzufügen. Mit anderen Worten, ich habe eine Kategorie in UITextField 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
Nun, so benutze ich es:
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 als nächstes in der Zeile steht.
Ich hoffe es hilft.
Hier ist eine ohne Delegation:
tf1.addTarget(tf2, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)
tf2.addTarget(tf3, action: #selector(becomeFirstResponder), for: .editingDidEndOnExit)
ObjC:
[tf1 addTarget:tf2 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
[tf2 addTarget:tf3 action:@selector(becomeFirstResponder) forControlEvents:UIControlEventEditingDidEndOnExit];
Funktioniert mit der (meist unbekannten) UIControlEventEditingDidEndOnExit
UITextField
-Aktion.
Sie können dies auch problemlos im Storyboard verknüpfen, sodass kein oderDelegierungscode erforderlich ist.
Edit: Eigentlich kann ich nicht herausfinden, wie ich das im Storyboard anschließen kann. becomeFirstResponder
scheint keine angebotene Aktion für dieses Kontrollereignis zu sein, was schade ist. Sie können jedoch alle Ihre Textfelder mit einer einzigen Aktion in Ihrem ViewController verknüpfen, die dann bestimmt, welches textField becomeFirstResponder
basierend auf dem Sender ist (obwohl es dann nicht so elegant ist wie die oben genannte programmatische Lösung, so dass IMO es mit dem über dem Code in viewDidLoad
).
Eine Swift-Erweiterung, die die Antwort von mxcl anwendet, um dies besonders einfach zu machen (angepasst an Swift 2.3 von Traveler):
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 den Return-Button für alle außer dem letzten Feld auf "Next" und für das letzte Feld auf "Done" und wechselt den Fokus bzw. die Tastatur, wenn diese getippt werden.
Swift <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: Verwendung wie folgt -
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)
}
}
Eine konsistentere und robustere Methode ist die Verwendung von NextResponderTextField Sie können sie vollständig vom Interface Builder aus konfigurieren, ohne dass Sie den Delegaten festlegen oder view.tag
verwenden müssen.
Alles was Sie tun müssen, ist
UITextField
auf NextResponderTextField
nextResponderField
so ein, dass er auf den nächsten Antwortenden zeigt. Dies kann eine beliebige UITextField
- oder eine beliebige UIResponder
-Unterklasse sein. Es kann auch ein UIButton sein und die Bibliothek ist intelligent genug, um das TouchUpInside
-Ereignis der Schaltfläche nur dann auszulösen, wenn sie aktiviert ist .Hier ist die Bibliothek in Aktion:
Ich mag die OO Lösungen, die bereits von Anth0 und Answerbot vorgeschlagen wurden. Ich arbeitete jedoch an einem schnellen und kleinen POC, daher wollte ich nicht mit Unterklassen und Kategorien herumspielen.
Eine andere einfache Lösung besteht darin, ein NSArray von Feldern zu erstellen und das nächste Feld nachzuschlagen, wenn Sie auf Weiter drücken. Keine OO Lösung, aber schnell, einfach und leicht zu implementieren. Außerdem können Sie die Bestellung 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;
}
Hier ist eine Implementierung von Tabs mit einer Kategorie in UIControl. Diese Lösung bietet alle Vorteile der Methoden von Michael und Anth0, funktioniert jedoch für alle UIControls und nicht nur für UITextField
s. 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;
}
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
Ich habe schon viele Codes ausprobiert und schließlich funktionierte das für mich in Swift 3.0 Latest [March 2017]
Die ViewController
-Klasse sollte von der UITextFieldDelegate
geerbt werden, damit dieser Code funktioniert.
class ViewController: UIViewController,UITextFieldDelegate
Fügen Sie das Textfeld mit der richtigen Tag-Nummer hinzu. Diese Tag-Nummer wird verwendet, um das Steuerelement auf der Grundlage der ihm zugewiesenen inkrementellen Tag-Nummer in das entsprechende Textfeld zu bringen.
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 wird durch die Angabe von returnKeyType = UIReturnKeyType.next
die Rücktaste der Tastatur als Next
angezeigt. Sie haben auch andere Optionen als Join/Go
usw., je nach Ihrer Anwendung ändern Sie die Werte.
Diese textFieldShouldReturn
ist eine von UITextFieldDelegate kontrollierte Methode. 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
}
Nachdem Sie ein Textfeld verlassen haben, rufen Sie [otherTextField becomeFirstResponder] auf und das nächste Feld erhält den Fokus.
Dies kann tatsächlich ein schwieriges Problem sein, mit dem man sich befassen muss, da häufig auch der Bildschirm gescrollt oder die Position des Textfelds auf andere Weise angepasst werden muss, um bei der Bearbeitung leicht zu sehen. Stellen Sie sicher, dass Sie viele Tests durchführen, indem Sie auf verschiedene Weise in die Textfelder ein- und aussteigen und auch früher gehen (geben Sie dem Benutzer immer die Möglichkeit, die Tastatur zu schließen, anstatt zum nächsten Feld zu gehen, normalerweise mit "Done" in die Navigationsleiste)
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
return YES;
}
Eine sehr einfache Methode, um die Tastatur zu schließen, wenn die Schaltfläche "Fertig" gedrückt wird, ist:
Erstellen Sie eine neue IBAction in der Kopfzeile
- (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 möchten, klicken Sie auf das Ereignis 'Did End On Exit'.
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 von Controller, zu entscheiden, welche Steuerung der nächste Ersthelfer sein soll.
Auch galten die meisten Antworten nur für die Navigation vorwärts, aber Benutzer möchten möglicherweise auch rückwärts navigieren.
Also hier ist, was ich mir ausgedacht habe. Ihr Formular sollte von einem Ansichtscontroller verwaltet werden, und Ansichtscontroller sind Teil der Antwortkette. Es steht Ihnen also völlig frei, die folgenden Methoden zu 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 für das Scrollen von zuvor sichtbaren Offscreen-Respondern.
Ein weiterer Vorteil dieses Ansatzes besteht darin, dass Sie nicht alle Arten von Steuerelementen, die Sie möglicherweise anzeigen möchten (wie UITextField
s), in Unterklassen unterteilen müssen, sondern stattdessen die Logik auf Controller-Ebene verwalten können der richtige Ort dazu.
Setzen Sie zuerst die Eingabetaste der Tastatur in xib, andernfalls können Sie Code in viewdidload
schreiben:
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;
}
Ich habe gerade einen neuen Pod erstellt, als ich mit diesem Zeug GNTextFieldsCollectionManager fertig wurde. 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 ein eigenes Array von textFields angeben.
Lösung in Swift 3.1, Nachdem Sie Ihre Textfelder verbunden haben, setzen IBOutlets Ihren Delegaten für Textfelder in viewDidLoad und navigieren Sie dann in textFieldShouldReturn durch Ihre 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
}
}
Hallo an alle bitte sehen dieses hier
- (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;
}
}
Ich habe versucht, dieses Problem mit einem komplexeren Ansatz zu lösen, der darauf basiert, jeder Zelle (oder UITextField
) einer UITableView
einen eindeutigen Tag-Wert zuzuweisen, der später abgerufen werden kann: enable-next-uitextfield-in-uitableview-ios
Ich hoffe das hilft!
Ich habe die Antwort von PeyloW hinzugefügt, falls Sie vorige/nächste Tastenfunktionen 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 unterteilen:
@implementation UIView (FindAndReturnFirstResponder)
- (UITextView *)findFirstResponderAndReturn
{
for (UITextView *subView in self.subviews) {
if (subView.isFirstResponder){
return subView;
}
}
return nil;
}
@end
Dies funktionierte für mich in Xamarin.iOS/Monotouch . Ändern Sie die Tastaturschaltfläche in Next, übergeben Sie das Steuerelement an das nächste UITextField und verbergen Sie die Tastatur nach dem letzten UITextField.
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.
};
}
}
Innerhalb von ViewDidLoad haben Sie:
Wenn für Ihre TextFields kein Tag festgelegt wurde:
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.
Dies ist eine einfache Lösung in Swift, ohne Verwendung eines Tags, 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 mit Ihrem Textfeld-Delegierten so (zum Beispiel):
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
textField.nextTextFieldField()?.becomeFirstResponder()
return true
}
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 führe ich einen einfachen Test für den Index des UITextFields aus, der meldet, dass der Benutzer auf getippt wurde:
// 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;
}
if (Zelle == null) { cell = [[UITableViewCell-Zuordnung] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: cellIdentifier]; txt_Input = [[UITextField-Zuordnung] initWithFrame: CGRectMake (0, 10, 150, 30)]; txt_Input.tag = indexPath.row + 1; [self.array_Textfields addObject: txt_Input]; // Initialisiere das veränderbare Array in ViewDidLoad } - (BOOL) textFieldShouldReturn: (UITextField *) textField { int tag = (int) textField.tag; UITextField * txt = [self.array_Textfields objectAtIndex: tag]; [txt becomeFirstResponder]; Rückgabe YES; }
Ich hatte mehr als 10 UITextField in meinem Storyboard, und die nächste Funktion wurde aktiviert, indem ein Array von UITextField erstellt wurde und das nächste UITextField zum ersten Antworter wurde. 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
Wenn jemand so will. Ich denke, dass dies den gestellten Anforderungen am nächsten kommt
So habe ich dieses umgesetzt
Fügen Sie für jedes Textfeld, für das Sie das Setup einrichten möchten, eine Zubehöransicht 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 Taps zu behandeln
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.
Ohne Usings-Tags und ohne Hinzufügen einer Eigenschaft für nextField/nextTextField können Sie Folgendes versuchen, TAB zu emulieren, wobei "testInput" Ihr aktuelles 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];
}];
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 die Tastatur nicht entfernt wird.
Ich verwende die Antwort von Michael G. Emmons seit etwa einem Jahr und funktioniert hervorragend. Ich habe kürzlich festgestellt, dass der Aufruf von resignFirstResponder und dann von comeFirstResponder sofort dazu führen kann, dass die Tastatur "stört", verschwindet und dann sofort erscheint. Ich habe seine Version leicht geändert, um den resignFirstResponder zu überspringen, wenn nextField verfügbar ist.
- (BOOL) textFieldShouldReturn: (UITextField *) textField { if ([textField isKindOfClass: [NRTextField-Klasse]])) { NRTextField * nText = (NRTextField *) textField; if ([nText nextField]! = null) { dispatch_async (dispatch_get_main_queue (), ^ {[[nText nextField] becomeFirstResponder];}); } sonst{ [textField resignFirstResponder]; } } sonst{ [textField resignFirstResponder]; } Rückgabe wahr; }
Hier ist eine Swift 3-Version von Anth0 's Antwort. Ich poste es hier, um allen Swift-Entwicklern zu helfen, seine großartige Antwort zu nutzen! Ich habe mir die Freiheit genommen, einen Rückgabetyp "Weiter" hinzuzufügen, wenn Sie das zugehörige Objekt festlegen.
extension UITextField {
@nonobjc static var NextHashKey: UniChar = 0
var nextTextField: UITextField? {
get {
return objc_getAssociatedObject(self,
&UITextField.NextHashKey) as? UITextField
}
set(next) {
self.returnKeyType = UIReturnKeyType.next
objc_setAssociatedObject(self,
&UITextField.NextHashKey,next,.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
Hier ist eine weitere Erweiterung, die eine Möglichkeit zeigt, den obigen Code zum Durchlaufen einer Liste von UITextFields zu verwenden.
extension UIViewController: UITextFieldDelegate {
public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
guard let next = textField.nextTextField else {
textField.resignFirstResponder()
return true
}
next.becomeFirstResponder()
return false
}
}
Und dann in Ihrem ViewController oder wo auch immer, Sie können Ihre Textfelder so einrichten ...
@IBOutlet fileprivate weak var textfield1: UITextField!
@IBOutlet fileprivate weak var textfield2: UITextField!
@IBOutlet fileprivate weak var textfield3: UITextField!
...
[textfield1, textfield2, textfield3].forEach{ $0?.delegate = self }
textfield1.nextTextField = textfield2
textfield2.nextTextField = textfield3
// We don't assign a nextTextField to textfield3 because we want
// textfield3 to be the last one and resignFirstResponder when
// the return button on the soft keyboard is tapped.
Dies ist ein alter Beitrag, hat aber einen hohen Seitenrang, also werde ich mit meiner Lösung einrasten.
Ich hatte ein ähnliches Problem und erstellte eine Unterklasse von UIToolbar
, um die nächste/vorherige/erledigte Funktionalität in einer dynamischen tableView mit Abschnitten zu verwalten: https://github.com/jday001/DataEntryToolbar
Sie legen die Symbolleiste als inputAccessoryView Ihrer Textfelder fest und fügen sie ihrem Wörterbuch hinzu. Auf diese Weise können Sie auch bei dynamischen Inhalten vorwärts und rückwärts durchlaufen. Es gibt Delegat-Methoden, wenn Sie Ihre eigene Funktionalität auslösen möchten, wenn die textField-Navigation stattfindet. Sie müssen sich jedoch nicht mit der Verwaltung von Tags oder dem Status der Ersthelfer beschäftigen.
Es gibt Code-Snippets und eine Beispiel-App am GitHub-Link, die Sie bei den Implementierungsdetails unterstützen. Sie benötigen ein eigenes Datenmodell, um die Werte in den Feldern zu verfolgen.
sie können dazu die IQKeyboardManager-Bibliothek verwenden. Es erledigt alles, Sie benötigen keine zusätzlichen Einstellungen. IQKeyboardManager ist über CocoaPods verfügbar. Zur Installation fügen Sie einfach die folgende Zeile in Ihre Pod-Datei ein:
pod 'IQKeyboardManager'
oder Ziehen Sie einfach das IQKeyBoardManager-Verzeichnis aus dem Demoprojekt in Ihr Projekt. Das ist es ... Sie finden das IQKeyBoardManager-Verzeichnis unter https://github.com/hackiftekhar/IQKeyboardManager
Ein sicherer und direkter Weg, vorausgesetzt:
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
}
}
Swift 3 Solution , mit geordnetem Array von UITextField's
func nextTextField() {
let textFields = // Your textfields array
for i in 0 ..< textFields.count{
if let textfield = textFields[i], textfield.isFirstResponder{
textfield.resignFirstResponder()
if i+1 < textFields.count, let nextextfield = textFields[i+1]{
nextextfield.becomeFirstResponder()
return
}
}
}
}