Existe-t-il un moyen (dans IB ou dans le code) de définir l'ordre de tabulation entre les champs de texte dans une vue?
Notez que je ne parle pas du champ suivant du formulaire après avoir appuyé sur le bouton de retour (ou "Suivant") - de nombreux claviers Bluetooth ont une touche de tabulation, qui semble parcourir les champs dans un ordre complètement différent. Dans mon cas particulier, cet ordre ne correspond pas à la position des champs dans la vue ni même à l'ordre dans lequel les champs ont été ajoutés. La modification manuelle du fichier xib pour changer NSNextKeyView ne semble pas non plus faire de différence.
Quelqu'un sait-il comment modifier cet ordre?
Je suis intéressé à résoudre le même problème, bien que jusqu'à présent, l'ordre par défaut, qui semble être de gauche à droite, puis de haut en bas, est celui que je veux.
J'ai testé l'hypothèse selon laquelle le curseur se déplace dans le premier ordre de profondeur à travers l'arborescence des sous-vues et de la supervision, mais c'est pas vrai. Changer l'ordre des sous-vues sans changer leur emplacement n'a pas changé l'ordre des champs traversés par tab presses.
Une fonctionnalité peut-être utile est que la méthode textFieldShouldBeginEditing du délégué de champ de texte semble être appelée pour chaque champ de texte dans la fenêtre de l'application. Si cela renvoie NON, alors le champ de texte ne sera pas choisi, donc si vous pouvez définir la commande souhaitée et ne renvoyer que la bonne commande OUI, cela pourrait résoudre votre problème.
La réponse de @ sprocket n'a été que quelque peu utile. Ce n'est pas parce que quelque chose fonctionne hors de la boîte que vous devriez arrêter de penser à une meilleure façon - ou même la bonne façon - de faire quelque chose. Comme il l'a remarqué, le comportement n'est pas documenté mais correspond à nos besoins la plupart du temps.
Cela ne me suffisait cependant pas. Pensez à un langage RTL et les onglets tabuleraient toujours de gauche à droite, sans oublier que le comportement est entièrement différent d'un simulateur à l'autre (l'appareil ne concentre pas la première entrée sur l'onglet). Plus important encore, l'implémentation non documentée d'Apple semble ne prendre en compte que les vues actuellement installées dans la hiérarchie des vues.
Pensez à un formulaire sous la forme (sans jeu de mots) d'une vue de table. Chaque cellule contient un seul contrôle, par conséquent, tous les éléments du formulaire ne peuvent pas être visibles en même temps. Apple reviendrait en arrière une fois que vous avez atteint le contrôle le plus bas (à l'écran!), Au lieu de faire défiler plus bas. Ce comportement n'est certainement pas ce que nous souhaitons.
Alors, voici ce que j'ai trouvé. Votre formulaire doit être géré par un contrôleur de vue et les contrôleurs de vue font partie de la chaîne de répondeurs. Vous êtes donc parfaitement libre de mettre en œuvre les méthodes suivantes:
#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];
}
Une logique supplémentaire pour faire défiler les répondeurs hors écran visibles à l'avance peut s'appliquer.
Un autre avantage de cette approche est que vous n'avez pas besoin de sous-classer tous les types de contrôles que vous voudrez peut-être afficher (comme UITextField
s) mais pouvez à la place gérer la logique au niveau du contrôleur, où, soyons honnêtes, c'est - au bon endroit pour le faire.
Le comportement de la touche Tab dans ios sera le suivant: - lorsque vous appuyez sur la touche tab du clavier externe - le contrôle parcourt tous les champs de texte de cet écran en appelant uniquement la méthode shouldBeginEditing où sa valeur de retour est également déterminée par Apple qui ne peut pas être remplacé. Après avoir scanné tous les champs, il calcule le champ de texte positionné en x le plus proche par rapport au décalage de la vue par rapport au champ de texte actuel, puis au champ positionné en Y le plus proche.
De plus, rien ne peut être fait tant que le contrôle n'est pas arrivé à la méthode textFieldDidBeginEditing.
La raison de la restriction d'Apple pourrait être de laisser les développeurs suivre les directives de l'interface utilisateur où le prochain répondeur du champ devrait être le champ le plus proche plutôt que tout autre champ.
Voici comment définir l'ordre des onglets sur iOS:
Enregistrez un UIKeyCommand pour détecter la touche de tabulation enfoncée. Je l'ai fait dans mon contrôleur de vue actuel.
self.addKeyCommand(UIKeyCommand(input: "\t", modifierFlags: [], action: #selector(tabKeyPressed)))
À l'intérieur du gestionnaire tabKeyPressed clé, recherchez votre champ actif actuel, puis définissez votre prochain répondeur. orderTextFields est un tableau de UITextField dans l'ordre de tabulation que je veux.
func tabKeyPressed(){
let activeField = getActiveField()
if(activeField == nil){
return
}
let nextResponder = getNextTextField(activeField!)
nextResponder?.becomeFirstResponder()
}
func getActiveField() -> UITextField? {
for textField in orderedTextFields {
if(textField.isFirstResponder()){
return textField
}
}
return nil
}
func getNextTextField(current: UITextField) -> UITextField? {
let index = orderedTextField.indexOf(current)
if(orderedTextField.count-1 <= index!){
return nil
}
return orderedTextField[index! + 1]
}
Vous pouvez le faire en définissant la balise pour chaque champ de texte et en le gérant dans la méthode textfieldShouldReturn.
Voir cet article de blog à ce sujet: http://iphoneincubator.com/blog/windows-views/how-to-create-a-data-entry-screen
J'ai résolu cela en sous-classant UITextField en tant que NextableTextField. Cette sous-classe a une propriété de classe UITextField avec IBOutlet un raccordement.
Construisez l'interface dans IB. Définissez la classe de votre champ de texte sur NextableTextField. Utilisez l'inspecteur de connexions pour faire glisser une connexion vers le champ "suivant" vers lequel vous voulez tabuler.
Dans votre classe de délégué de champ de texte, ajoutez cette méthode de délégué ...
- (BOOL)textFieldShouldReturn:(UITextField *) textField
{
BOOL didResign = [textField resignFirstResponder];
if (!didResign) return NO;
if ([textField isKindOfClass:[NextableTextField class]])
dispatch_async(dispatch_get_current_queue(), ^{ [[(NextableTextField *)textField nextField] becomeFirstResponder]; });
return YES;
}
BTW - Je ne suis pas venu avec cela; souviens-toi juste d'avoir vu l'idée de quelqu'un d'autre.
Le seul moyen que j'ai trouvé pour détecter de manière unique une touche de tabulation à partir d'un clavier physique consiste à implémenter la méthode insertText: du protocole UIKeyInput sur un objet personnalisé qui peutBecomeFirstResponder.
- (void)insertText:(NSString *)text {
NSLog(@"text is equal to tab character: %i", [text isEqualToString:@"\t"]);
}
Je n'ai pas réussi à faire fonctionner cela en sous-classant UITextField, malheureusement, car UITextField ne permettra pas à la méthode insertText: protocol d'être appelée.
Cela pourrait vous aider sur le chemin, cependant ...