La tastiera iPad non si chiuderà se lo stile di presentazione modale di ViewController è UIModalPresentationFormSheet


214

Nota:

Vedi la risposta accettata (non la più votata) per la soluzione a partire da iOS 4.3.

Questa domanda riguarda un comportamento scoperto nella tastiera dell'iPad, in cui si rifiuta di essere respinto se mostrato in una finestra di dialogo modale con un controller di navigazione.

Fondamentalmente, se presento il controller di navigazione con la seguente riga come di seguito:

navigationController.modalPresentationStyle = UIModalPresentationFormSheet;

La tastiera rifiuta di essere respinta. Se commento questa riga, la tastiera scompare bene.

...

Ho due campi di testo, nome utente e password; il nome utente ha un pulsante Avanti e la password ha un pulsante Fine. La tastiera non sparirà se la presento in un controller di navigazione modale.

LAVORI

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

NON FUNZIONA

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

Se rimuovo la parte del controller di navigazione e presento 'b' come controller di visualizzazione modale da sola, funziona. Il controller di navigazione è il problema?

LAVORI

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

LAVORI

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

La seguente domanda SO sembra avere lo stesso problema, ma non ci sono risposte: stackoverflow.com/questions/3019709/…
Kalle

+1 Grazie per la tua grande spiegazione. Ma dove devo mettere quel metodo? Sembra che non funzioni dove creo il codice per presentare il controller del modello ...
Lorenzo B,

1
Deve trovarsi nella classe controller della vista modale stessa.
Kalle,

Grazie. Vedo. Ho risolto inserendolo in una categoria per UINavigationControllerclasse. Saluti.
Lorenzo B,

Sono così in debito con te per questa domanda. Sono rimasto sorpreso dal fatto che resignFirstResponderfosse in esecuzione ma la tastiera è ancora visualizzata. Il mio scenario (presentationFormSheet con navigazione contrllr) è esattamente lo stesso del tuo. Grazie mille !!
sErVerdevIL

Risposte:


115

Nel controller di visualizzazione presentato in modo modale, basta eseguire disablesAutomaticKeyboardDismissall' override per restituire NO:

- (BOOL)disablesAutomaticKeyboardDismissal {
    return NO;
}

Sì, dal 4.3 sembra che sia così. Aggiornerà la domanda. Grazie!
Kalle

2
Questo deve essere aggiunto al controller di navigazione
carne in vaso

1
Sì, funziona quando lo sovrascrivi in ​​NavigationController. Questa è l'unica cosa che ha funzionato per me.
James Laurenstin,

Salvavita! Perché Apple fa cose del genere? Sicuramente dovrebbe essere NO e permetterci di cambiarlo se vogliamo davvero
SomaMan

Non funzionante sulla classe derivata da UIViewController, disabilita la
tastiera automatica Dismissal

172

Questo è stato classificato come "funziona come previsto" dagli ingegneri Apple. Ho presentato un bug per questo qualche tempo fa. Il loro ragionamento è che l'utente sta spesso inserendo i dati in una forma modale, quindi stanno cercando di essere "utili" e mantenere la tastiera visibile dove normalmente varie transizioni all'interno della vista modale possono far sì che la tastiera venga mostrata / nascosta ripetutamente.

modifica: ecco la risposta di un ingegnere Apple sui forum degli sviluppatori:

La tua vista è stata presentata per caso con lo stile UIModalPresentationFormSheet? Per evitare frequenti animazioni in-and-out, la tastiera a volte rimane sullo schermo anche quando non esiste un primo risponditore. Questo non è un bug.

Ciò sta causando molti problemi alle persone (me compreso) ma al momento non sembra esserci un modo per aggirare il problema.

AGGIORNARE:

In iOS 4.3 e versioni successive, ora è possibile implementare `-disablesAutomaticKeyboardDismissal 'sul controller della vista per restituire NO:

- (BOOL)disablesAutomaticKeyboardDismissal {
    return NO;
}

Questo risolve il problema.


7
pausa Wow, ok. Grazie mille per il testa a testa. Accidenti a Apple .. :(
Kalle

Hai inviato una segnalazione di bug ad Apple? L'ho fatto con ID 8384423. Ho anche presentato una domanda di esempio per riprodurre il comportamento.
Shaggy Frog

3
A partire da iOS 4.3 ora esiste un metodo disablesAutomaticKeyboardDismissal che risolve questo problema.
Kalle

5
provo che disabilita il metodo AutomaticKeyboardDismissal, ma non ha ancora risolto il problema, come risolverlo?
R. Dewi,

3
@Snips: è necessario creare una UINavigationControllersottoclasse che sostituisca disablesAutomaticKeyboardDismissalper restituirla NOe utilizzarla come controller di navigazione quando si presenta un foglio di modulo modale. Vedi la risposta da @ miha-hribar di seguito.
Pascal,

149

Fai attenzione se stai visualizzando il modale con a UINavigationController. È quindi necessario impostare il disablesAutomaticKeyboardDismissalcontroller di navigazione e non il controller di visualizzazione. Puoi farlo facilmente con le categorie.

File: UINavigationController + KeyboardDismiss.h

#import <Foundation/Foundation.h>

@interface UINavigationController (KeyboardDismiss)

- (BOOL)disablesAutomaticKeyboardDismissal;

@end

File: UINavigationController + KeyboardDismiss.m

#import "UINavigationController+KeyboardDismiss.h"

@implementation UINavigationController(KeyboardDismiss)

- (BOOL)disablesAutomaticKeyboardDismissal
{
    return NO;
}

@end

Non dimenticare di importare la categoria nel file in cui si utilizza UINavigationController.


19
+1, finalmente vedo evidenziate le informazioni mancanti per questo problema: che è necessario eseguire l'override disablesAutomaticKeyboardDismissaldi UINavigationController, non il proprio controller di visualizzazione, per risolvere questo problema.
DarkDust,

Bello! Proprio quello di cui avevo bisogno. Grazie.
Giustino,

Perfetto. Non chiaro dai documenti ufficiali, ma ha senso a causa del fatto che UINavigationController si trova nella catena di responder. Risposta eccellente. Grazie!
imnk,

1
Sto presentando una finestra di dialogo modale da un UISplitViewController. Ho provato il codice sopra, ma ho sostituito UISplitViewController con UINavigationController, ma non funziona ancora. Questo metodo dovrebbe funzionare anche su un UISplitViewController?
Taglia il

6
Non è una buona idea implementare un metodo duplicato in una categoria. Non puoi mai essere sicuro di quale implementazione verrà chiamata, quindi nella migliore delle ipotesi puoi aspettarti un comportamento incoerente. Meglio ereditare da UINavigationController e sovrascrivere il metodo nella classe personalizzata.
sean woodward,

51

Ho risolto questo problema usando lo UIModalPresentationPageSheetstile di presentazione e ridimensionandolo immediatamente dopo averlo presentato. Così:

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 ... questo non è del tutto giusto ... il ridimensionamento fa sì che il modale dipinga divertente ... è come se schiaccia il contenuto per adattarlo alla nuova casella di dimensioni o qualcosa del genere ... tutto sembra divertente. :(
toofah

Ci sono anche problemi di rotazione con questo ... se ruoti mentre questo modale è in alto si ridurrà / crescerà come se fosse una visualizzazione a pagina intera
toofah

2
toofah, ho modificato il codice per affrontare il problema di riduzione / crescita durante la rotazione; solo per dare alla superview un margine superiore e inferiore flessibile. Non sono sicuro di vedere l'altro comportamento.
Azdev,

1
funziona solo fino a quando non si spinge un'altra vista in cima a questa. Perché quando chiudi la vista sopra la vista presentata UIModalPresentationPageSheet, torna alla sua dimensione originale.
V1ru8,

Ha funzionato. Ma la parola nella vista sembra un po 'sfocata. Non so perché.
jeswang,

1

Se si attiva o disattiva un altro display modale, è possibile far scomparire la tastiera. Non è carino e non si anima, ma puoi farlo andare via.

Sarebbe bello se ci fosse una soluzione, ma per ora funziona. Puoi incastrarlo in una categoria UIViewControllere chiamarlo quando vuoi che la tastiera scompaia:

@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

Fai attenzione a questo mentre vedi viewDidAppear / viewDidDisappear e tutti questi metodi vengono chiamati. Come ho detto, non è carino, ma funziona.

-Adamo


1

Puoi anche aggirare questo problema in un'app universale semplicemente controllando il linguaggio e se si tratta di un iPad, non far apparire la tastiera automaticamente e lasciare che l'utente tocchi tutto ciò che desidera modificare.

Potrebbe non essere la soluzione migliore, ma è molto semplice e non ha bisogno di alcun trucco di fantasia che si romperà con la prossima versione principale di iOS :)


1

Metti questo codice nella tua vista. WillDisappear: il metodo del controller corrente è un altro modo per risolvere questo problema:

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

1

Ho scoperto che disablesAutomaticKeyboardDismissale l'aggiunta di una disablesAutomaticKeyboardDismissalfunzione non ha funzionato per il mioUITextField in una finestra di dialogo modale.

La tastiera su schermo non sarebbe andata via.

La mia soluzione era disabilitare tutti i controlli di immissione del testo nella mia finestra di dialogo, quindi riattivare quelli pertinenti una frazione di secondo dopo.

Sembra che quando iOS vede che nessuno dei UITextFieldcontrolli sono abilitati, allora non sbarazzarsi della tastiera.


0

Sono sicuro che hai esaminato questo, ma sei sicuro che la tua classe controller sia correttamente collegata come delegato UITextField, giusto?


L'ho impostato manualmente e vengono chiamati i metodi delegati, quindi sì.
Kalle

0

forse non restituire NO, ma SÌ. Quindi può andare via.

E hai anche un textFieldShouldEndEditingritorno SÌ?

E perché stai sparando [nextResponder becomeFirstResponder]?! mi dispiace ora vedo

Ho anche un numero di UITextViews che hanno tutte la proprietà "modificabile" impostata su FALSE.

Possiamo supporre che nessuno di loro, per caso, abbia un tagvalore di secondField.tag+1? In tal caso, stai dicendo loro di diventare il primo soccorritore, invece di dimettersi dal primo soccorritore. Magari metti un po 'di NSLog () se struttura.


1
NO = non inserire newline, da quello che posso dire. E impostandolo su SÌ non è stato risolto.
Kalle

1
Un UITextField, essendo una linea per definizione, non fa molto con le nuove linee, penso. Quindi si tratta più di elaborare premendo il pulsante Return / Done, come indicato nei documenti.
mvds,

Sei sicuro di aver collegato tutto nel modo giusto? Hai inserito NSLog("tf %x / method ...",textField);tutte le funzioni delegate?
mvds

Bene, le funzioni del delegato sono chiamate in modo appropriato e non lo sarebbero se il delegato non fosse impostato in modo appropriato. E NSLog fornisce un EXC_BAD_ACCESS. Inoltre mi avverte che è un tipo incompatibile in XCode.
Kalle

D'oh. Scusa, avrei dovuto vederlo da solo. Ho aggiornato la risposta sopra con i risultati di questi NSLogs poiché la formattazione si farà da sola in comunicazione.
Kalle

0

Per coloro che hanno problemi con UINavigationController, vedi la mia risposta a una domanda simile qui: https://stackoverflow.com/a/10507689/321785

Modifica: lo considero un miglioramento della soluzione di Miha Hribar (dal momento che la decisione è presa dove dovrebbe), e al contrario del commento di Pascal su una categoria su UIViewController


0

potrebbe non essere una soluzione perfetta, ma funziona
[self.view endEditing: YES];
ovunque il tuo pulsante o gesto sia implementato per presentare modale


0
Swift 4.1:
extension UINavigationController {
   override open var disablesAutomaticKeyboardDismissal: Bool {
      return false
   }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.