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.
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:
UITextField
s sont sur la même vue parente.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!
Il existe une beaucoup solution plus élégante qui m'a époustouflé la première fois que je l'ai vue. Avantages:
UITextField
et UITextView
- ou avec n'importe quelle commande d'interface utilisateur d'entrée au clavierCré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.
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.
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.
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
).
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)
}
}
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
UITextField
sur NextResponderTextField
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é .Voici la bibliothèque en action:
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;
}
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 UITextField
s. 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;
}
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
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/Go
etc, 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
}
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;
}
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'.
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;
}
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.
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
}
}
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;
}
}
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!
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
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.
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
}
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;
}
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; }
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
Si quelqu'un veut comme ça. Je pense que c'est le plus proche des exigences demandées en question
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.
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];
}];
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.
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; }
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.
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.
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
Un moyen plus sûr et plus direct, en supposant que:
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 , 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
}
}
}
}