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


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

Le dernier champ de texte doit fermer le clavier.

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

J'ai implémenté l'action textFieldShouldReturn, mais maintenant, les boutons Suivant et Terminé ferment le clavier.


461
2017-08-28 15:34


origine


Réponses:


Dans Cocoa pour Mac OS X, vous avez la prochaine chaîne de répondeurs, où vous pouvez demander au champ de texte quel contrôle devrait avoir le focus suivant. C'est ce qui fait fonctionner la tabulation entre les champs de texte. Mais comme les appareils iOS n’ont pas de clavier, que du toucher, ce concept n’a pas survécu à la transition vers Cocoa Touch.

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

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

En supposant que vous pouvez remplacer textFieldShouldReturn: comme 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 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 en superposition du champ de texte sera UITableViewCell, le prochain répondeur sera

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

544
2017-08-29 10:30



Il y a un beaucoup solution plus élégante qui m'a épaté la première fois que je l'ai vu. Avantages:

  • Plus proche de l'implémentation du champ de texte OSX où un champ de texte sait où le focus doit aller
  • Ne repose pas sur la définition ou l'utilisation de balises - qui sont, IMO fragile pour ce cas d'utilisation
  • Peut être étendu pour travailler avec les deux UITextField et UITextView contrôles - ou tout contrôle de l'interface utilisateur d'entrée au clavier
  • Ne pas encombrer votre contrôleur de vue avec le code de délégué UITextField
  • S'intègre parfaitement avec IB et peut être configuré via l'option-glisser-déposer familière pour connecter les prises.

Créer une sous-classe UITextField qui a un IBOutlet propriété appelée nextField. Voici l'en-tête:

@interface SOTextField : UITextField

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

@end

Et voici la mise en œuvre:

@implementation SOTextField

@end

Dans votre contrôleur de vue, vous allez créer le -textFieldShouldReturn: méthode délégué:

- (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, modifiez vos UITextFields pour utiliser le SOTextField classe. Ensuite, également dans IB, définissez le délégué pour chacun des 'SOTextFields' sur 'Propriétaire du fichier' (ce qui est juste là où vous placez le code pour la méthode déléguée - textFieldShouldReturn). La beauté de cette conception est que maintenant vous pouvez simplement cliquer avec le bouton droit sur n'importe quel textField et attribuer la sortie nextField à la suivante. SOTextField objet que vous voulez être le prochain répondeur.

Assigning nextField in IB

De plus, vous pouvez faire des choses sympas comme boucler les textFields de sorte qu'après que le dernier a perdu le focus, le premier recevra de nouveau le focus.

Cela peut facilement être étendu pour attribuer automatiquement le returnKeyType du SOTextField à un UIReturnKeyNext s'il y a un champ suivant assigné - une chose moins configurer manuellement.


169
2018-05-04 21:00



Voici ma solution pour ce problème.

Pour résoudre ce problème (et parce que je déteste me fier aux balises pour faire les 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


78
2017-12-01 16:34



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 avec le (principalement inconnu) UIControlEventEditingDidEndOnExit  UITextField action.

Vous pouvez également accrocher facilement ceci dans le storyboard, donc pas de délégation ou le code est requis

Edit: en fait, je n'arrive pas à comprendre comment créer un 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 associer tous vos champs de texte à une seule action dans votre ViewController, qui détermine ensuite quel textField becomeFirstResponder basé sur l'expéditeur (mais alors ce n'est pas aussi élégant que la solution programmatique ci-dessus, donc IMO le fait avec le code ci-dessus dans viewDidLoad).


72
2018-02-28 21:59



Une extension rapide qui applique la réponse de mxcl pour le rendre 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éplacera le focus sur le clavier lorsque ceux-ci seront tapé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 ça -

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

43
2018-03-09 15:38



Une façon plus cohérente et robuste est d'utiliser NextResponderTextField Vous pouvez le configurer entièrement à partir du générateur d'interface sans avoir besoin de définir le délégué ou d'utiliser view.tag.

Tout ce que vous devez faire est

  1. Définissez le type de classe de votre UITextField être NextResponderTextField enter image description here
  2. Ensuite, réglez la sortie du nextResponderField pour pointer vers l'intervenant suivant, cela peut être n'importe quoi UITextField ou tout UIResponder sous-classe. Cela peut être aussi un UIButton et la bibliothèque est assez intelligente pour déclencher la TouchUpInside événement du bouton uniquement s'il est activé. enter image description here enter image description here

Voici la bibliothèque en action:

enter image description here


25
2018-06-29 04:39



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

Une autre solution simple consiste à créer un champ NSArray et à rechercher le champ suivant lorsque vous appuyez sur suivant. Pas une solution OO, mais rapide, simple et facile à mettre en œuvre. En outre, vous pouvez voir et modifier la commande 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 renvoie toujours NO car je ne veux pas qu'un saut de ligne soit inséré. Je pensais juste que je le signalerais, car lorsque je suis revenu sur OUI, il sortait automatiquement des champs suivants ou insérait 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 est nécessaire pour rendre le champ invisible depuis le clavier. Si vous avez un code similaire, assurez-vous d'affecter le champ activeField avant de changer le premier répondeur. Changer de premier répondeur est immédiat et déclenchera l'événement KeyboardWasShown immédiatement.

14
2018-05-31 15:12