Le clavier de l'iPad ne sera pas ignoré si le style de présentation modal de ViewController est UIModalPresentationFormSheet


214

Remarque:

Voir la réponse acceptée (pas la plus votée) pour la solution à partir d'iOS 4.3.

Cette question concerne un comportement découvert sur le clavier de l'iPad, où il refuse d'être ignoré s'il est affiché dans une boîte de dialogue modale avec un contrôleur de navigation.

Fondamentalement, si je présente le contrôleur de navigation avec la ligne suivante comme ci-dessous:

navigationController.modalPresentationStyle = UIModalPresentationFormSheet;

Le clavier refuse d'être renvoyé. Si je commente cette ligne, le clavier s'en va bien.

...

J'ai deux textFields, nom d'utilisateur et mot de passe; le nom d'utilisateur a un bouton Suivant et le mot de passe a un bouton Terminé. Le clavier ne disparaîtra pas si je le présente dans un contrôleur de navigation modale.

TRAVAUX

broken *b = [[broken alloc] initWithNibName:@"broken" bundle:nil];
[self.view addSubview:b.view];

NE MARCHE PAS

broken *b = [[broken alloc] initWithNibName:@"broken" bundle:nil];
UINavigationController *navigationController = 
[[UINavigationController alloc]
 initWithRootViewController:b];
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
navigationController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:navigationController animated:YES];
[navigationController release];
[b release];

Si je retire la partie contrôleur de navigation et présente «b» comme contrôleur de vue modale par lui-même, cela fonctionne. Le contrôleur de navigation est-il le problème?

TRAVAUX

broken *b = [[broken alloc] initWithNibName:@"broken" bundle:nil];
b.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:b animated:YES];
[b release];

TRAVAUX

broken *b = [[broken alloc] initWithNibName:@"broken" bundle:nil];
UINavigationController *navigationController = 
    [[UINavigationController alloc]
         initWithRootViewController:b];
[self presentModalViewController:navigationController animated:YES];
[navigationController release];
[b release];

La question SO suivante semble avoir le même problème, mais il n'y a pas de réponse: stackoverflow.com/questions/3019709/…
Kalle

+1 Merci pour votre excellente explication. Mais où dois-je mettre cette méthode? Il semble que cela ne fonctionne pas là où je crée le code pour présenter le contrôleur de modèle ...
Lorenzo B

1
Il doit être dans la classe du contrôleur de vue modale lui-même.
Kalle

Merci. Je vois. J'ai résolu de le mettre dans une catégorie pour la UINavigationControllerclasse. À votre santé.
Lorenzo B

Je vous suis tellement redevable de cette question. J'ai été surpris de constater resignFirstResponderque le clavier était en cours d'exécution, mais le clavier était toujours affiché. Mon scénario (presentationFormSheet avec navig contrllr) est exactement le même que le vôtre. Merci beaucoup!!
sErVerdevIL

Réponses:


115

Dans le contrôleur de vue présenté de manière modale, remplacez simplement disablesAutomaticKeyboardDismissalpour retourner NO:

- (BOOL)disablesAutomaticKeyboardDismissal {
    return NO;
}

Oui, depuis la 4.3, cela semble être le cas. Met à jour la question. Merci!
Kalle

2
Cela doit être ajouté au contrôleur de navigation
pottedmeat

1
Oui, fonctionne lorsque vous le remplacez dans le NavigationController. C'est la seule chose qui a vraiment fonctionné pour moi.
James Laurenstin

Épargnant de vie! Pourquoi Apple fait-il des trucs comme ça? Il devrait sûrement être réglé par défaut sur NO et nous permettre de le changer si nous le voulons vraiment
SomaMan

Ne fonctionne pas sur la classe dérivée UIViewController, disablesAutomaticKeyboardDismissal n'est jamais appelé
Jorge Arimany

172

Cela a été classé comme "fonctionne comme prévu" par les ingénieurs d'Apple. J'ai déposé un bug pour cela il y a quelque temps. Leur raisonnement est que l'utilisateur va souvent entrer des données sous une forme modale, il essaie donc d'être "utile" et de garder le clavier visible là où d'ordinaire diverses transitions dans la vue modale peuvent provoquer l'affichage / le masquage répétitif du clavier.

edit: voici la réponse d'un ingénieur Apple sur les forums développeurs:

Votre vue a-t-elle été présentée par hasard avec le style UIModalPresentationFormSheet? Pour éviter les animations fréquentes d'entrée et de sortie, le clavier reste parfois à l'écran même lorsqu'il n'y a pas de premier répondant. Ce n'est pas un bug.

Cela pose beaucoup de problèmes aux gens (moi y compris) mais pour le moment il ne semble pas y avoir de moyen de contourner ce problème.

METTRE À JOUR:

Dans iOS 4.3 et versions ultérieures, vous pouvez désormais implémenter `-disablesAutomaticKeyboardDismissal 'sur votre contrôleur de vue pour retourner NON:

- (BOOL)disablesAutomaticKeyboardDismissal {
    return NO;
}

Cela résout le problème.


7
pause Wow, d'accord. Merci beaucoup pour l'avertissement. Damn Apple .. :(
Kalle

Avez-vous soumis un rapport de bogue à Apple? Je l'ai fait sous l'ID # 8384423. J'ai également soumis un exemple d'application pour reproduire le comportement.
Shaggy Frog

3
Depuis iOS 4.3, il existe désormais une méthode disablesAutomaticKeyboardDismissal qui résout ce problème.
Kalle

5
J'essaie de désactiver la méthode AutomaticKeyboardDismissal, mais cela n'a toujours pas résolu le problème, comment le résoudre?
R. Dewi

3
@Snips: Vous devez créer une UINavigationControllersous-classe qui remplace disablesAutomaticKeyboardDismissalpour revenir NOet l'utiliser comme contrôleur de navigation lorsque vous présentez une feuille de formulaire modale. Voir la réponse de @ miha-hribar ci-dessous.
Pascal

149

Soyez prudent si vous affichez le modal avec un UINavigationController. Vous devez ensuite définir le disablesAutomaticKeyboardDismissalsur le contrôleur de navigation et non sur le contrôleur de vue. Vous pouvez facilement le faire avec des catégories.

Fichier: UINavigationController + KeyboardDismiss.h

#import <Foundation/Foundation.h>

@interface UINavigationController (KeyboardDismiss)

- (BOOL)disablesAutomaticKeyboardDismissal;

@end

Fichier: UINavigationController + KeyboardDismiss.m

#import "UINavigationController+KeyboardDismiss.h"

@implementation UINavigationController(KeyboardDismiss)

- (BOOL)disablesAutomaticKeyboardDismissal
{
    return NO;
}

@end

N'oubliez pas d'importer la catégorie dans le fichier où vous utilisez le UINavigationController.


19
+1, enfin je vois la pièce manquante d'information pour cette question mis en évidence: que l' on doit passer outre disablesAutomaticKeyboardDismissalde UINavigationController, et non le contrôleur propre point de vue, pour résoudre ce problème.
DarkDust

Agréable! Juste ce dont j'avais besoin. Je vous remercie.
Justin

Parfait. Pas clair à partir des documents officiels, mais cela a du sens car l'UINavigationController fait partie de la chaîne de répondeurs. Excellente réponse. Je vous remercie!
imnk

1
Je présente une boîte de dialogue modale à partir d'un UISplitViewController. J'ai essayé le code ci-dessus, mais j'ai substitué UISplitViewController à UINavigationController, mais cela ne fonctionne toujours pas. Cette méthode doit-elle également fonctionner sur un UISplitViewController?
Snips

6
Ce n'est pas une bonne idée d'implémenter une méthode en double dans une catégorie. Vous ne pouvez jamais être certain de l'implémentation qui sera appelée, donc au mieux vous pouvez vous attendre à un comportement incohérent. Mieux vaut hériter de UINavigationController et remplacer la méthode dans votre classe personnalisée.
sean woodward

51

J'ai résolu ce problème en utilisant le UIModalPresentationPageSheetstyle de présentation et en le redimensionnant immédiatement après l'avoir présenté. Ainsi:

viewController.modalPresentationStyle = UIModalPresentationPageSheet;
viewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:viewController animated:YES];
viewController.view.superview.autoresizingMask = 
    UIViewAutoresizingFlexibleTopMargin | 
    UIViewAutoresizingFlexibleBottomMargin;    
viewController.view.superview.frame = CGRectMake(
    viewController.view.superview.frame.origin.x,
    viewController.view.superview.frame.origin.y,
    540.0f,
    529.0f
);
viewController.view.superview.center = self.view.center;
[viewController release];

Hmmm ... ce n'est pas tout à fait raison ... le redimensionnement fait que le modal peint drôle ... c'est comme s'il écrase le contenu pour qu'il tienne dans la nouvelle boîte de taille ou quelque chose ... tout a l'air drôle. :(
toofah

Il y a aussi des problèmes de rotation avec celui-ci ... si vous tournez alors que ce modal est en place, il rétrécira / augmentera comme s'il s'agissait d'une vue pleine page
toofah

2
toofah, j'ai édité le code pour faire face au problème de rétrécissement / croissance lors de la rotation; juste une question de donner à la superview une marge flexible supérieure et inférieure. Je ne suis pas sûr de voir l'autre comportement.
azdev

1
cela ne fonctionne que tant que vous n'appuyez pas sur une autre vue par-dessus celle-ci. Parce que lorsque vous fermez la vue poussée au-dessus de la vue présentée UIModalPresentationPageSheet, elle revient à sa taille d'origine.
V1ru8

Ça a marché. Mais le mot dans la vue semble un peu flou. Je ne sais pas pourquoi.
jeswang

1

Si vous basculez sur un affichage modal différent, vous pouvez faire disparaître le clavier. Ce n'est pas joli et ça n'anime pas, mais vous pouvez le faire disparaître.

Ce serait génial s'il y avait un correctif, mais pour l'instant cela fonctionne. Vous pouvez le caler dans une catégorie UIViewControlleret l'appeler quand vous voulez que le clavier disparaisse:

@interface _TempUIVC : UIViewController
@end

@implementation _TempUIVC
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return YES;
}
@end

@implementation UIViewController (Helpers)

- (void)_dismissModalViewController {
    [self dismissModalViewControllerAnimated:NO];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
    [self release];
}

- (void)forceKeyboardDismissUsingModalToggle:(BOOL)animated {
    [self retain];
    _TempUIVC *tuivc = [[_TempUIVC alloc] init];
    tuivc.modalPresentationStyle = UIModalPresentationCurrentContext;
    [self presentModalViewController:tuivc animated:animated];
    if (animated) {
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_dismissModalViewController) name:UIKeyboardDidHideNotification object:nil];
    } else
        [self _dismissModalViewController];
    [tuivc release];
}

@end

Soyez cependant prudent lorsque vous affichez ViewDidAppear / viewDidDisappear et toutes ces méthodes sont appelées. Comme je l'ai dit, ce n'est pas joli, mais ça marche.

-Adam


1

Vous pouvez également contourner cela dans une application universelle en vérifiant simplement l'idiome et s'il s'agit d'un iPad, ne faites pas apparaître le clavier automatiquement et laissez l'utilisateur appuyer sur tout ce qu'il souhaite modifier.

Ce n'est peut-être pas la meilleure solution, mais c'est très simple et n'a pas besoin de hacks sophistiqués qui rompront avec la prochaine version majeure d'iOS :)


1

Mettez ce code dans votre viewWillDisappear: la méthode du contrôleur actuel est une autre façon de résoudre ce problème:

Class UIKeyboardImpl = NSClassFromString(@"UIKeyboardImpl");
id activeInstance = [UIKeyboardImpl performSelector:@selector(activeInstance)];
[activeInstance performSelector:@selector(dismissKeyboard)];

1

J'ai trouvé que l' disablesAutomaticKeyboardDismissalajout d'une disablesAutomaticKeyboardDismissalfonction ne fonctionnait pas pour monUITextField dans une boîte de dialogue modale.

Le clavier à l'écran ne disparaîtrait tout simplement pas.

Ma solution était de désactiver tous les contrôles de saisie de texte dans ma boîte de dialogue, puis de réactiver les contrôles pertinents une fraction de seconde plus tard.

Il semble que lorsque iOS voit qu'aucun des UITextFieldcontrôles sont activés, il ne se débarrasser du clavier.


0

Je suis sûr que vous avez regardé cela, mais vous êtes sûr que votre classe de contrôleur est correctement connectée en tant que délégué UITextField, non?


Je l'ai défini moi-même manuellement, et les méthodes déléguées sont appelées, alors oui.
Kalle

0

peut - être ne retournez pas NON, mais OUI. Cela peut donc disparaître.

Et vous avez également un textFieldShouldEndEditingOUI de retour?

Et pourquoi tirez-vous [nextResponder becomeFirstResponder]?! désolé je vois maintenant

J'ai également un certain nombre de UITextViews qui ont tous leur propriété "modifiable" définie sur FAUX.

Pouvons-nous supposer qu'aucun d'entre eux, par hasard, n'a une tagvaleur de secondField.tag+1? Si c'est le cas, vous leur dites de devenir le premier répondant, au lieu de démissionner du premier répondant. Peut-être mettre un peu NSLog () dans cette structure if.


1
NON = ne pas insérer de nouvelle ligne, d'après ce que je peux dire. Et le régler sur OUI ne l'a pas corrigé.
Kalle

1
Un UITextField, étant une ligne par définition, ne fait pas grand chose avec les nouvelles lignes, je pense. Il s'agit donc davantage de traiter en appuyant sur le bouton Retour / Terminé, comme indiqué dans les documents.
mvds

Êtes-vous très sûr d'avoir tout connecté correctement? Avez-vous mis un NSLog("tf %x / method ...",textField);dans toutes les fonctions de délégué?
mvds

Eh bien, les fonctions de délégué sont appelées de manière appropriée, et elles ne le seraient pas si le délégué n'était pas configuré correctement. Et le NSLog donne un EXC_BAD_ACCESS. M'avertit également qu'il s'agit d'un type incompatible dans XCode.
Kalle

D'oh. Désolé, j'aurais dû le voir moi-même. J'ai mis à jour la réponse ci-dessus avec les résultats de ces NSLogs puisque le formatage se gook en comm ..
Kalle

0

Pour ceux qui ont des problèmes avec UINavigationController, voir ma réponse à une question similaire ici: https://stackoverflow.com/a/10507689/321785

Edit: je considère cela comme une amélioration de la solution de Miha Hribar (puisque la décision se déroule là où elle devrait), et contrairement au commentaire de Pascal concernant une catégorie sur UIViewController


0

peut ne pas être une solution parfaite, mais fonctionne
[self.view endEditing: YES];
d'où que votre bouton ou geste soit implémenté pour présenter le modal


0
Swift 4.1:
extension UINavigationController {
   override open var disablesAutomaticKeyboardDismissal: Bool {
      return false
   }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.