web-dev-qa-db-fra.com

Comment naviguer dans les champs de texte (boutons Suivant/Terminé)

Comment puis-je naviguer dans tous mes champs de texte avec le bouton "Suivant" du clavier iPhone?

Le dernier champ de texte devrait fermer le clavier.

J'ai configuré les boutons de l'IB (Next/Done) mais maintenant je suis bloqué.

J'ai implémenté l'action textFieldShouldReturn mais maintenant les boutons Next et Done ferment le clavier.

469
phx

Dans Cocoa pour Mac OS X, vous avez la chaîne de répondeurs suivante, dans laquelle vous pouvez demander au champ de texte quel contrôle doit avoir le focus ensuite. C’est ce qui fait que la tabulation entre les champs de texte fonctionne. Mais comme les appareils iOS n’ont pas de clavier, seulement tactile, ce concept n’a pas survécu à la transition vers Cocoa Touch.

Cela peut être fait de toute façon, avec deux hypothèses:

  1. Tous les "tabables" UITextFields sont sur la même vue parente.
  2. Leur "ordre de tabulation" est défini par la propriété de la balise.

En supposant que vous puissiez remplacer textFieldShouldReturn: par ceci:

-(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.
}

Ajoutez un peu plus de code et les hypothèses peuvent également être ignorées.

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
}

Si la vue d'ensemble du champ de texte sera un UITableViewCell, le prochain répondeur sera

let nextResponder = textField.superview?.superview?.superview?.viewWithTag(nextTag) as UIResponder!
557
PeyloW

Il existe une beaucoup solution plus élégante qui m'a époustouflé la première fois que je l'ai vue. Avantages:

  • Plus proche de l'implémentation du champ de texte OSX où un champ de texte sait où le focus devrait aller ensuite
  • Ne comptez pas sur la définition ou l’utilisation de balises, qui sont, IMO, fragiles pour ce cas d’utilisation.
  • Peut être étendu pour fonctionner avec les deux commandes UITextField et UITextView - ou avec n'importe quelle commande d'interface utilisateur d'entrée au clavier
  • N'encombrez pas votre contrôleur de vue avec le code délégué UITextField standard
  • S'intègre parfaitement à IB et peut être configuré via l'option-glisser-déposer habituelle pour connecter les prises.

Créez une sous-classe UITextField qui possède une propriété IBOutlet appelée nextField. Voici l'en-tête:

@interface SOTextField : UITextField

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

@end

Et voici l'implémentation:

@implementation SOTextField

@end

Dans votre contrôleur de vue, vous allez créer la méthode de délégation -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;
}

Dans IB, changez vos UITextFields pour qu'ils utilisent la classe SOTextField. Ensuite, également dans IB, définissez le délégué pour chaque propriétaire 'Fichier' de SOTextFields '(c'est-à-dire où vous insérez le code de la méthode déléguée - textFieldShouldReturn). La beauté de cette conception réside dans le fait que vous pouvez maintenant simplement cliquer avec le bouton droit de la souris sur n’importe quel textField et attribuer le point de vente nextField à l’objet SOTextField suivant que vous souhaitez être le prochain répondeur.

Assigning nextField in IB

De plus, vous pouvez faire des choses sympas, comme boucler les champs de texte de manière à ce que, une fois le dernier point perdu, le premier le reçoive à nouveau.

Cela peut facilement être étendu pour affecter automatiquement le returnKeyType du SOTextField à un UIReturnKeyNext s'il existe un nextField attribué - une chose de moins à configurer manuellement.

170
memmons

Voici ma solution pour ce problème.

Pour résoudre ce problème (et parce que je déteste utiliser des balises pour faire des choses), j'ai décidé d'ajouter une propriété personnalisée à l'objet UITextField. En d'autres termes, j'ai créé une catégorie sur UITextField comme ceci:

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

Maintenant, voici comment je l'utilise:

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

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

Et implémentez la méthode textFieldShouldReturn:

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

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

    return NO; 
}

J'ai maintenant une sorte de liste chaînée de UITextField, chacun sachant qui est le suivant dans la ligne.

J'espère que ça va aider.

78
Anth0

En voici une sans délégation:

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];

Fonctionne en utilisant l’action UIControlEventEditingDidEndOnExit (généralement inconnue) UITextField.

Vous pouvez également facilement le connecter au storyboard, de sorte qu'aucun code de délégation ou n'est requis.

Edit: en fait, je ne peux pas comprendre comment lier ceci au storyboard. becomeFirstResponder ne semble pas être une action proposée pour cet événement de contrôle, ce qui est dommage. Néanmoins, vous pouvez relier tous vos champs de texte à une seule action dans votre ViewController, ce qui détermine ensuite le champ textField à becomeFirstResponder en fonction de l'expéditeur (bien que ce ne soit pas aussi élégant que la solution de programmation ci-dessus, IMO le fait avec le code ci-dessus dans viewDidLoad ).

76
mxcl

Une extension Swift qui applique la réponse de mxcl pour rendre cela particulièrement facile (adapté à Swift 2.3 par 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)
    }
}

C'est facile à utiliser:

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

L'extension définira le bouton de retour sur "Suivant" pour tous les champs sauf le dernier et sur "Terminé" pour le dernier champ, et décaler le focus/fermer le clavier lorsque ceux-ci sont activés.

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: Utiliser comme ceci - 

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)
        }
    }
45
Amos Joshua

Une méthode plus cohérente et robuste consiste à utiliser NextResponderTextField Vous pouvez le configurer totalement à partir du générateur d’interface sans avoir besoin de définir le délégué ni d’utiliser view.tag.

Tout ce que vous devez faire c'est 

  1. Définissez le type de classe de votre UITextField sur NextResponderTextFieldenter image description here
  2. Ensuite, configurez le point de vente de nextResponderField pour qu'il pointe vers le prochain répondeur. Il peut s'agir de n'importe quelle UITextField ou de toute sous-classe UIResponder. Il peut s'agir également d'un bouton UIB et la bibliothèque est suffisamment intelligente pour déclencher l'événement TouchUpInside du bouton uniquement s'il est activé .enter image description hereenter image description here

Voici la bibliothèque en action:

enter image description here

25
mohamede1945

J'aime les solutions OO déjà suggérées par Anth0 et Answerbot. Cependant, je travaillais sur un petit et rapide POC, je ne voulais donc pas encombrer les choses de sous-classes et de catégories.

Une autre solution simple consiste à créer un tableau NSArray de champs et à rechercher le champ suivant lorsque vous appuierez sur suivant. Pas une solution OO, mais rapide, simple et facile à mettre en œuvre. En outre, vous pouvez voir et modifier l'ordre en un coup d'œil. 

Voici mon code (construit sur d'autres réponses dans ce fil):

@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;
}
  • Je retourne toujours NON parce que je ne veux pas de saut de ligne inséré. Je pensais juste que je ferais remarquer que depuis que je suis retourné, il quitterait automatiquement les champs suivants ou insérerait un saut de ligne dans mon TextView. Il m'a fallu un peu de temps pour comprendre cela.
  • activeField garde la trace du champ actif au cas où le défilement serait nécessaire pour le masquer du clavier. Si vous avez un code similaire, assurez-vous d’affecter le champ activeField avant de changer le premier répondant. Le changement du premier intervenant est immédiat et déclenche immédiatement l'événement KeyboardWasShown.
14
Marquee

Voici une implémentation de la tabulation utilisant une catégorie sur UIControl. Cette solution présente tous les avantages des méthodes de Michael et Anth0, mais fonctionne pour tous les contrôles UIControls, pas seulement UITextFields. Il fonctionne également de manière transparente avec Interface Builder et les storyboards.

Application source et exemple: Dépôt GitHub pour UIControlsWithTabbing

Usage:

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

Assigning nextControl in Interface Builder

Entête:

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

#import <UIKit/UIKit.h>

@interface UIControl (NextControl)

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

- (BOOL)transferFirstResponderToNextControl;

@end

La mise en oeuvre:

#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
11
picciano

J'ai essayé de nombreux codes et finalement, cela a fonctionné pour moi dans Swift 3.0 Latest [March 2017]

La classe ViewController doit hériter de la UITextFieldDelegate pour que ce code fonctionne.

class ViewController: UIViewController,UITextFieldDelegate  

Ajoutez le champ de texte avec le numéro de balise approprié. Ce numéro de balise est utilisé pour amener le contrôle au champ de texte approprié en fonction du numéro de balise incrémentiel qui lui est attribué.

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

Dans le code ci-dessus, le returnKeyType = UIReturnKeyType.next où obligera la touche de retour du clavier à afficher en tant que Next, vous avez également d'autres options en tant que Join/Goetc, en fonction de votre application, modifiez les valeurs.

This textFieldShouldReturn est une méthode de UITextFieldDelegate contrôlée et nous avons ici la sélection de champ suivante basée sur l'incrémentation de la valeur de Tag

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

Une fois que vous quittez un champ de texte, vous appelez [otherTextField devientFirstResponder] et le champ suivant devient actif.

Cela peut en fait être un problème délicat à traiter car vous voudrez souvent faire défiler l'écran ou ajuster la position du champ de texte pour qu'il soit facile à voir lors de l'édition. Assurez-vous simplement de faire beaucoup de tests avec l'entrée et la sortie des champs de texte de différentes manières et en laissant également tôt (donnez toujours à l'utilisateur la possibilité de désactiver le clavier au lieu d'aller au champ suivant, généralement avec "Terminé" dans la barre de navigation) 

 -(BOOL)textFieldShouldReturn:(UITextField *)textField
{
   [[self.view viewWithTag:textField.tag+1] becomeFirstResponder];
   return YES;
}
9
iKushal

Une méthode très simple pour supprimer le clavier lorsque vous appuyez sur le bouton "Terminé" est la suivante:

Créer un nouvel IBAction dans l'en-tête

- (IBAction)textFieldDoneEditing:(id)sender;

Dans le fichier d'implémentation (fichier .m), ajoutez la méthode suivante:

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

Ensuite, lorsque vous lierez IBAction au champ de texte, vous lierez à l'événement 'Did End On Exit'.

7
jcrowson

Commencez par définir la touche de retour du clavier dans xib, sinon vous pouvez écrire du code dans 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;
}
6
mahesh chowdary

Je viens de créer un nouveau Pod quand je traite de ce genre de choses GNTextFieldsCollectionManager . Il gère automatiquement le problème suivant/dernier textField et est très facile à utiliser:

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

Grabs tous les champs de texte triés en apparaissant dans la hiérarchie de la vue (ou par balises), ou vous pouvez spécifier votre propre tableau de textFields.

4
JakubKnejzlik

Solution dans Swift 3.1, après avoir connecté vos champs de texte, IBOutlets définit votre délégué de champs de texte dans viewDidLoad, puis parcourez votre action dans textFieldShouldReturn

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
        }

    }
4
Krishna Thakur

Bonjour à tous s'il vous plaît voir celui-ci

- (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;    
  }
}
4
rithik

J'ai essayé de résoudre ce problème en utilisant une approche plus sophistiquée basée sur l'affectation de chaque cellule (ou UITextField) dans un UITableView à une valeur de balise unique pouvant être récupérée ultérieurement: activate-next-uitextfield-in-uitableview-ios

J'espère que ça aide!

4
Fabiano Francesconi

J'ai ajouté à la réponse de PeyloW si vous souhaitez implémenter une fonctionnalité de bouton précédent/suivant:

- (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];

    }
}

Où vous sous-classez le UIView comme ceci:

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

Cela a fonctionné pour moi dans Xamarin.iOS/Monotouch . Réglez le bouton du clavier sur Next, passez le contrôle au prochain UITextField et masquez le clavier après le dernier 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.
    };
  }
}

Dans le ViewDidLoad vous aurez:

Si vos champs de texte n'ont pas de balise, définissez-le maintenant:

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

et juste l'appel

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.
3
Daniele D.

C'est une solution simple dans Swift, sans utilisation de tag, pas de tricks de storyboard.

Il suffit d'utiliser cette extension:

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
    }

}

Et appelez-le comme ceci (par exemple) avec votre délégué de champ de texte:

func textFieldShouldReturn(textField: UITextField) -> Bool {
    textField.resignFirstResponder()
    textField.nextTextFieldField()?.becomeFirstResponder()
    return true
}
3
Kevin ABRIOUX

Je préfère plutôt:

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

Dans le fichier NIB, j'attache les textFields dans l'ordre souhaité à ce tableau inputFields. Après cela, je fais un test simple pour l'index de UITextField qui indique que l'utilisateur a tapé sur return:

// 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;
}
3
Anticro
if (cell == nil) 
 {
 cell = [[UITableViewCell alloc]] 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]; // Initialise un tableau mutable dans ViewDidLoad 
} 

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

 int tag = (int) textField.tag; 
 UITextField * txt = [self.array_Textfields objectAtIndex: tag]; 
 [txt devientFirstResponder]; 
 retourner OUI; 
} 
3
Rebecca Curties

J'avais environ 10+ UITextField dans mon scénario et la façon dont j'ai activé la fonctionnalité suivante consistait à créer un tableau UITextField et à faire du prochain UITextField le premier répondeur. Voici le fichier d'implémentation:

#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
3
Illegal Argument

Si quelqu'un veut comme ça. Je pense que c'est le plus proche des exigences demandées en question 

 enter image description here

Voici comment j'ai implémenté celui-ci

Ajoutez une vue accessoire pour chaque champ de texte pour lequel vous voulez configurer, en utilisant 

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
}

Utilisez les fonctions suivantes pour gérer les robinets 

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
}

Vous devrez donner les balises textFields dans l'ordre dans lequel vous souhaitez que le bouton next/prev réponde. 

3
Mohammad Sadiq

Sans balises usings et sans ajouter de propriété pour nextField/nextTextField, vous pouvez essayer ceci pour émuler TAB, où "testInput" est votre champ actif actuel:

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];
                                                    }];
2
Cœur

dans textFieldShouldReturn, vous devriez vérifier que le champ de texte sur lequel vous vous trouvez actuellement n'est pas le dernier lorsqu'il cliquera ensuite et s'il ne doit pas ignorer le clavier.

2
Daniel

J'utilise la réponse de Michael G. Emmons depuis environ un an maintenant, cela fonctionne très bien. J'ai récemment remarqué que le fait d'appeler resignFirstResponder puis de devenirFirstResponder immédiatement pouvait provoquer un "problème" au clavier, disparaître puis apparaître immédiatement. J'ai légèrement changé sa version pour ignorer le resignFirstResponder si le prochain champ est disponible.

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

 if ([textField isKindOfClass: [classe NRTextField]])) 
 {
 NRTextField * nText = (NRTextField *) textField; 
 if ([nText nextField]! = nil) {
 dispatch_async (dispatch_get_main_queue (), .________ } 
 autre{
 [textField resignFirstResponder]; 
 } 
 } 
 autre{
 [textField resignFirstResponder]; 
 } 

 return true; 

} 
2
arinmorf

Voici une version Swift 3 de la réponse de Anth0 . Je le poste ici pour aider les développeurs de Swift à tirer parti de sa réponse géniale! J'ai pris la liberté d'ajouter un type de clé de retour de "Suivant" lorsque vous définissez l'objet associé.

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

Voici une autre extension qui montre la possibilité d'utiliser le code ci-dessus pour parcourir la liste de UITextFields.

extension UIViewController: UITextFieldDelegate {
 public func textFieldShouldReturn(_ textField: UITextField) -> Bool {
   guard let next = textField.nextTextField else {
     textField.resignFirstResponder()
     return true
   }

    next.becomeFirstResponder()
    return false
  }
}

Et ensuite, dans votre ViewController ou ailleurs, vous pouvez configurer vos champs de texte comme suit ...

@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.
2
Justin Wright

Ceci est un ancien post, mais a un rang de page élevé, donc je vais vous donner ma solution.

J'ai eu un problème similaire et j'ai fini par créer une sous-classe de UIToolbar pour gérer la fonctionnalité next/previous/done dans une tableView dynamique avec des sections: https://github.com/jday001/DataEntryToolbar

Vous définissez la barre d'outils en tant qu'entrée inputAccessoryView de vos champs de texte et les ajoutez à son dictionnaire. Cela vous permet de les parcourir en avant et en arrière, même avec un contenu dynamique. Il existe des méthodes de délégation si vous souhaitez déclencher votre propre fonctionnalité lorsque la navigation textField est activée, mais vous n'avez pas à gérer de balises ni le statut de premier répondant.

Des extraits de code et un exemple d'application sont disponibles sur le lien GitHub pour vous aider avec les détails de la mise en œuvre. Vous aurez besoin de votre propre modèle de données pour garder une trace des valeurs à l'intérieur des champs.

2
jday

vous pouvez utiliser la bibliothèque IQKeyboardManager pour le faire. Il gère tout, vous n'avez besoin d'aucune configuration supplémentaire. IQKeyboardManager est disponible via CocoaPods. Pour l'installer, ajoutez simplement la ligne suivante à votre fichier Podfile:

pod 'IQKeyboardManager'

ou Il vous suffit de faire glisser le répertoire IQKeyBoardManager du projet de démonstration vers votre projet. C'est ça ... vous pouvez trouver le répertoire IQKeyBoardManager à partir de https://github.com/hackiftekhar/IQKeyboardManager

2
Amit Shelgaonkar

Un moyen plus sûr et plus direct, en supposant que:

  • les délégués de champ de texte sont définis sur votre contrôleur de vue
  • tous les champs de texte sont des sous-vues de la même vue
  • les champs de texte ont des balises dans l'ordre dans lequel vous souhaitez progresser (par exemple, textField2.tag = 2, textField3.tag = 3, etc.)
  • le passage au champ de texte suivant survient lorsque vous appuyez sur le bouton return du clavier (vous pouvez le changer en next, done, etc.)
  • vous voulez que le clavier disparaisse après le dernier champ de texte

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

    }
}
1
jason z

Swift 3 Solution , utilisation du tableau ordonné de 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
            }
        }
    }
}
0
Maor