El teclado del iPad no se descartará si el estilo de presentación modal de ViewController es UIModalPresentationFormSheet

214

Nota:

Vea la respuesta aceptada (no la más votada) para la solución a partir de iOS 4.3.

Esta pregunta es sobre un comportamiento descubierto en el teclado del iPad, donde se niega a ser descartado si se muestra en un diálogo modal con un controlador de navegación.

Básicamente, si presento el controlador de navegación con la siguiente línea de la siguiente manera:

navigationController.modalPresentationStyle = UIModalPresentationFormSheet;

El teclado se niega a ser descartado. Si comento esta línea, el teclado desaparece bien.

...

Tengo dos campos de texto, nombre de usuario y contraseña; el nombre de usuario tiene un botón Siguiente y la contraseña tiene un botón Listo. El teclado no desaparecerá si presento esto en un controlador de navegación modal.

TRABAJOS

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

NO FUNCIONA

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 elimino la parte del controlador de navegación y presento 'b' como un controlador de vista modal por sí mismo, funciona. ¿Es el controlador de navegación el problema?

TRABAJOS

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

TRABAJOS

broken *b = [[broken alloc] initWithNibName:@"broken" bundle:nil];
UINavigationController *navigationController = 
    [[UINavigationController alloc]
         initWithRootViewController:b];
[self presentModalViewController:navigationController animated:YES];
[navigationController release];
[b release];
Kalle
fuente
La siguiente pregunta SO parece tener el mismo problema, pero no hay respuestas: stackoverflow.com/questions/3019709/…
Kalle
+1 Gracias por tu gran explicación. Pero, ¿dónde tengo que poner ese método? Parece que no funciona donde creo el código para presentar el controlador del modelo ...
Lorenzo B
1
Tiene que estar en la clase modal del controlador de vista en sí.
Kalle
Gracias. Veo. Resolví ponerlo en una categoría para la UINavigationControllerclase. Salud.
Lorenzo B
Estoy tan en deuda con usted por esta pregunta. Me sorprendió que se resignFirstResponderestuviera ejecutando pero el teclado aún se mostraba. Mi escenario (presentationFormSheet con navig contrllr) es exactamente el mismo que el suyo. ¡¡Gracias una tonelada!!
sErVerdevIL

Respuestas:

115

En el controlador de vista que se presenta modalmente, simplemente anule disablesAutomaticKeyboardDismissalpara devolver NO:

- (BOOL)disablesAutomaticKeyboardDismissal {
    return NO;
}
Sebastian H
fuente
Sí, desde 4.3 este parece ser el caso. Se actualizará la pregunta. ¡Gracias!
Kalle
2
Esto debe agregarse al controlador de navegación
pottedmeat
1
Sí, funciona cuando lo sobrescribe en el NavigationController. Eso es lo único que realmente funcionó para mí.
James Laurenstin
Salvavidas! ¿Por qué Apple hace cosas como esta? Seguramente debería tener el valor predeterminado NO y permitirnos cambiarlo si realmente queremos
SomaMan
No funciona en la clase derivada de UIViewController, deshabilitaAutomaticKeyboardDismissal nunca se llama
Jorge Arimany
172

Esto ha sido clasificado como "funciona según lo previsto" por los ingenieros de Apple. Archivé un error para esto hace un tiempo. Su razonamiento es que el usuario a menudo va a ingresar datos en una forma modal, por lo que intentan ser "útiles" y mantener el teclado visible donde normalmente varias transiciones dentro de la vista modal pueden hacer que el teclado se muestre / oculte repetidamente.

editar: aquí está la respuesta de un ingeniero de Apple en los foros de desarrolladores:

¿Tu vista fue presentada por casualidad con el estilo UIModalPresentationFormSheet? Para evitar animaciones frecuentes de entrada y salida, el teclado a veces permanecerá en la pantalla incluso cuando no haya un primer respondedor. Esto no es un error.

Esto le está dando problemas a muchas personas (incluido yo mismo) pero en este momento no parece haber una forma de solucionarlo.

ACTUALIZAR:

En iOS 4.3 y versiones posteriores, ahora puede implementar `-disablesAutomaticKeyboardDismissal 'en su controlador de vista para devolver NO:

- (BOOL)disablesAutomaticKeyboardDismissal {
    return NO;
}

Esto soluciona el problema.

Mike Weller
fuente
77
pausa Wow, está bien. Muchas gracias por el aviso. Damn Apple .. :(
Kalle
¿Enviaste un informe de error a Apple? Lo hice con el ID # 8384423. También presenté una solicitud de muestra para reproducir el comportamiento.
Shaggy Frog
3
A partir de iOS 4.3 ahora hay un método disablesAutomaticKeyboardDismissal que soluciona este problema.
Kalle
55
Intento que deshabilita el método de teclado automático automático, pero aún así no resolvió el problema, ¿cómo resolverlo?
R. Dewi
3
@Snips: debe crear una UINavigationControllersubclase que se anule disablesAutomaticKeyboardDismissalpara devolver NOy usar esto como controlador de navegación cuando presente una hoja de formulario modal. Vea la respuesta de @ miha-hribar a continuación.
Pascal
149

Tenga cuidado si está mostrando el modal con a UINavigationController. Luego debe configurarlo disablesAutomaticKeyboardDismissalen el controlador de navegación y no en el controlador de vista. Puedes hacer esto fácilmente con categorías.

Archivo: UINavigationController + KeyboardDismiss.h

#import <Foundation/Foundation.h>

@interface UINavigationController (KeyboardDismiss)

- (BOOL)disablesAutomaticKeyboardDismissal;

@end

Archivo: UINavigationController + KeyboardDismiss.m

#import "UINavigationController+KeyboardDismiss.h"

@implementation UINavigationController(KeyboardDismiss)

- (BOOL)disablesAutomaticKeyboardDismissal
{
    return NO;
}

@end

No olvide importar la categoría en el archivo donde usa UINavigationController.

Miha Hribar
fuente
19
1, por fin ver la pieza que falta de la información para este problema puesto de manifiesto: que uno tiene que anulan disablesAutomaticKeyboardDismissalde UINavigationController, no el propio controlador de vista, para solucionar este problema.
DarkDust
¡Agradable! Justo lo que necesitaba. Gracias.
Justin
Perfecto. No está claro en los documentos oficiales, pero tiene sentido debido a que UINavigationController está en la cadena de respuesta. Excelente respuesta ¡Gracias!
imnk
1
Estoy presentando un diálogo modal desde un UISplitViewController. Intenté el código anterior, pero sustituí UISplitViewController por UINavigationController, pero aún no funciona. ¿Este método también debería funcionar en un UISplitViewController?
Snips
66
No es una buena idea implementar un método duplicado en una categoría. Nunca puede estar seguro de qué implementación se llamará, por lo que, en el mejor de los casos, puede esperar un comportamiento inconsistente. Es mejor heredar de UINavigationController y anular el método en su clase personalizada.
Sean Woodward
51

Resolví esto usando el UIModalPresentationPageSheetestilo de presentación y redimensionándolo inmediatamente después de presentarlo. Al igual que:

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];
azdev
fuente
Hmmm ... esto no está del todo bien ... el cambio de tamaño hace que el modal pinte divertido ... es como si aplastara el contenido para que quepa en el nuevo cuadro de tamaño o algo así ... todo parece divertido. :(
toofah
También hay problemas de rotación con este ... si gira mientras este modal está activo, se reducirá / crecerá como si fuera una vista de página completa
toofah
2
Toofah, edité el código para tratar el problema de reducción / crecimiento al rotar; solo es cuestión de darle a la supervista un margen superior e inferior flexible. No estoy seguro de ver el otro comportamiento.
azdev
1
esto funciona solo mientras no presione otra vista encima de esta. Porque cuando cierra la vista empujada por encima de la vista presentada por UIModalPresentationPageSheet, vuelve a su tamaño original.
V1ru8
Funcionó. Pero la palabra en la vista se ve un poco borrosa. No se porque.
jeswang
1

Si alterna una pantalla modal diferente, puede hacer que el teclado desaparezca. No es bonito y no se anima, pero puedes hacer que desaparezca.

Sería genial si hubiera una solución, pero por ahora esto funciona. Puedes ponerlo en una categoría UIViewControllery llamarlo cuando quieras que el teclado desaparezca:

@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

Sin embargo, tenga cuidado con esto cuando veaDidAppear / viewDidDisappear y se invoquen todos esos métodos. Como dije, no es bonito, pero funciona.

-Adán

Adán
fuente
1

También puede solucionar esto en una aplicación universal simplemente comprobando el idioma y, si se trata de un iPad, no abra el teclado automáticamente y deje que el usuario toque lo que quiera editar.

Puede que no sea la mejor solución, pero es muy sencillo y no necesita ningún truco elegante que rompa con la próxima versión principal de iOS :)

Maciej Swic
fuente
1

Ponga este código en su vista WillDisappear: el método del controlador actual es otra forma de solucionar esto:

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

Encontré eso disablesAutomaticKeyboardDismissaly agregar una disablesAutomaticKeyboardDismissalfunción no funcionó para miUITextField en un diálogo modal.

El teclado en pantalla simplemente no desaparecería.

Mi solución fue deshabilitar todos los controles de entrada de texto en mi cuadro de diálogo, y luego volver a habilitar los relevantes una fracción de segundo más tarde.

Parece que cuando iOS ve que ninguno de los UITextFieldcontroles están habilitados, entonces no deshacerse del teclado.

Mike Gledhill
fuente
0

Estoy seguro de que ha visto esto, pero está seguro de que su clase de controlador está correctamente conectada como delegado de UITextField, ¿verdad?

Neal L
fuente
Lo configuré manualmente, y los métodos de delegado se llaman, así que sí.
Kalle
0

tal vez no devuelva NO, pero SÍ. Entonces puede desaparecer.

¿Y también tienes un textFieldShouldEndEditingSI?

¿ Y por qué estás disparando [nextResponder becomeFirstResponder]? lo siento, veo ahora

También tengo un número de UITextViews que tienen su propiedad "editable" establecida en FALSE.

¿Podemos suponer que ninguno de ellos, por casualidad, tiene un tagvalor de secondField.tag+1? Si es así, les está diciendo que se conviertan en el primer respondedor, en lugar de renunciar al primer respondedor. Tal vez ponga algo de NSLog () en esa estructura if.

mvds
fuente
1
NO = no inserte nueva línea, por lo que puedo decir. Y establecerlo en SÍ no lo solucionó.
Kalle
1
Un UITextField, siendo una línea por definición, no hace mucho con las nuevas líneas, creo. Por lo tanto, se trata más de procesar presionar el botón Volver / Listo, como se indica en los documentos.
mvds
¿Estás seguro de haber conectado todo de la manera correcta? ¿Ha puesto un NSLog("tf %x / method ...",textField);en todas las funciones de delegado?
mvds
Bueno, las funciones de delegado se invocan de manera adecuada, y no lo serían si el delegado no estuviera configurado adecuadamente. Y el NSLog da un EXC_BAD_ACCESS. También me advierte acerca de que sea un tipo incompatible en XCode.
Kalle
D'oh Lo siento, debería haberlo visto yo mismo. He actualizado la respuesta anterior con los resultados de estos NSLogs Ya que el formateo se gook sí en comunicación ..
Kalle
0

Para aquellos que tienen problemas con UINavigationController, vea mi respuesta a una pregunta similar aquí: https://stackoverflow.com/a/10507689/321785

Editar: Considero que esto es una mejora para la solución de Miha Hribar (ya que la decisión se está tomando donde debería), y a diferencia del comentario de Pascal con respecto a una categoría en UIViewController

Chris Trahey
fuente
0

puede no ser una solución perfecta, pero funciona
[self.view endEditing: YES];
desde donde se implementa su botón o gesto para presentar modal

Tanuj Jagoori
fuente
0
Swift 4.1:
extension UINavigationController {
   override open var disablesAutomaticKeyboardDismissal: Bool {
      return false
   }
}
Fivewood
fuente