O teclado do iPad não será descartado se o estilo de apresentação modal do ViewController for UIModalPresentationFormSheet

214

Nota:

Consulte a resposta aceita (que não é a mais votada) para a solução a partir do iOS 4.3.

Esta pergunta é sobre um comportamento descoberto no teclado do iPad, onde se recusa a ser descartado se mostrado em uma caixa de diálogo modal com um controlador de navegação.

Basicamente, se eu apresentar o controlador de navegação com a seguinte linha, como abaixo:

navigationController.modalPresentationStyle = UIModalPresentationFormSheet;

O teclado se recusa a ser descartado. Se eu comentar esta linha, o teclado desaparecerá bem.

...

Eu tenho dois campos de texto, nome de usuário e senha; o nome de usuário possui o botão Avançar e a senha o botão Concluído. O teclado não desaparecerá se eu apresentar isso em um controlador de navegação modal.

TRABALHO

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

NÃO 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];

Se eu remover a parte do controlador de navegação e apresentar 'b' como um controlador de exibição modal por si só, ele funcionará. O controlador de navegação é o problema?

TRABALHO

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

TRABALHO

broken *b = [[broken alloc] initWithNibName:@"broken" bundle:nil];
UINavigationController *navigationController = 
    [[UINavigationController alloc]
         initWithRootViewController:b];
[self presentModalViewController:navigationController animated:YES];
[navigationController release];
[b release];
Kalle
fonte
A seguinte pergunta do SO parece estar tendo o mesmo problema, mas não há respostas: stackoverflow.com/questions/3019709/…
Kalle
+1 Obrigado por sua ótima explicação. Mas onde eu tenho que colocar esse método? Parece que não está funcionando onde eu crio o código para apresentar o controlador de modelo ...
Lorenzo B
1
Ele deve estar na própria classe de controlador de exibição modal.
precisa
Obrigado. Entendo. Eu resolvi colocá-lo em uma categoria para a UINavigationControlleraula. Felicidades.
Lorenzo B
Sou muito grato a você por esta pergunta. Fiquei surpreso ao resignFirstResponderestar executando, mas o teclado ainda está sendo mostrado. Meu cenário (presentationFormSheet com navig contrllr) é exatamente o mesmo que o seu. Muito obrigado !!
sErVerdevIL

Respostas:

115

No controlador de exibição que é apresentado modalmente, basta substituir disablesAutomaticKeyboardDismissalpara retornar NO:

- (BOOL)disablesAutomaticKeyboardDismissal {
    return NO;
}
Sebastian H
fonte
Sim, desde 4.3, este parece ser o caso. Atualizará a pergunta. Obrigado!
Kalle
2
Isso precisa ser adicionado ao controlador de navegação
pottedmeat
1
Sim, funciona quando você o substitui no NavigationController. Essa é a única coisa que realmente funcionou para mim.
James Laurenstin
Poupança de vida! Por que a Apple faz coisas assim? Certamente deve padrão para NO e nos permitem alterá-lo se realmente queremos
SomaMan
Não trabalhando em classe UIViewController derivada, disablesAutomaticKeyboardDismissal nunca é chamado
Jorge Arimany
172

Isso foi classificado como "funciona como pretendido" pelos engenheiros da Apple. Eu registrei um bug por isso há algum tempo. O raciocínio deles é que o usuário geralmente inserirá dados de forma modal, de modo a tentar ser "útil" e manter o teclado visível onde normalmente várias transições na exibição modal podem fazer com que o teclado seja exibido / oculto repetidamente.

edit: aqui está a resposta de um engenheiro da Apple nos fóruns de desenvolvedores:

Por acaso, sua visão foi apresentada com o estilo UIModalPresentationFormSheet? Para evitar animações frequentes de entrada e saída, o teclado às vezes permanece na tela, mesmo quando não há um responsável pela resposta. Isso não é um bug.

Isso está causando muitos problemas para as pessoas (inclusive eu), mas no momento não parece haver uma maneira de contornar isso.

ATUALIZAR:

No iOS 4.3 e posterior, agora você pode implementar `-disablesAutomaticKeyboardDismissal 'no seu controlador de exibição para retornar NO:

- (BOOL)disablesAutomaticKeyboardDismissal {
    return NO;
}

Isso corrige o problema.

Mike Weller
fonte
7
pausa Uau, tudo bem. Muito obrigado pelo aviso. Maldição da Apple .. :(
Kalle
Você enviou um relatório de bug à Apple? Fiz isso sob o ID # 8384423. Também enviei um aplicativo de amostra para reproduzir o comportamento.
Shaggy Frog
3
No iOS 4.3, agora existe um método disablesAutomaticKeyboardDismissal que corrige esse problema.
Kalle
5
Eu tento que disablesAutomaticKeyboardDismissal método, mas ainda não resolveu o problema, como resolvê-lo?
R. Dewi
3
@ Snips: você precisa criar uma UINavigationControllersubclasse que substitua disablesAutomaticKeyboardDismissalpara retornar NOe usá-la como controlador de navegação quando apresentar uma folha de formulário modal. Veja a resposta de @ miha-hribar abaixo.
27412 Pascal
149

Tenha cuidado se estiver exibindo o modal com a UINavigationController. Você precisa definir o disablesAutomaticKeyboardDismissalno controlador de navegação e não no controlador de exibição. Você pode fazer isso facilmente com categorias.

Arquivo: UINavigationController + KeyboardDismiss.h

#import <Foundation/Foundation.h>

@interface UINavigationController (KeyboardDismiss)

- (BOOL)disablesAutomaticKeyboardDismissal;

@end

Arquivo: UINavigationController + KeyboardDismiss.m

#import "UINavigationController+KeyboardDismiss.h"

@implementation UINavigationController(KeyboardDismiss)

- (BOOL)disablesAutomaticKeyboardDismissal
{
    return NO;
}

@end

Não esqueça de importar a categoria no arquivo em que você usa o UINavigationController.

Miha Hribar
fonte
19
+1, finalmente vejo a peça que falta de informação para este problema destacou: que é preciso substituir disablesAutomaticKeyboardDismissalde UINavigationController, não o próprio controlador de vista, para corrigir esse problema.
DarkDust 26/10/11
Agradável! Apenas o que eu precisava. Obrigado.
23711 Justin
Perfeito. Não está claro nos documentos oficiais, mas faz sentido porque o UINavigationController está na cadeia de respostas. Excelente resposta. Obrigado!
Imnk 17/04
1
Estou apresentando uma caixa de diálogo modal de um UISplitViewController. Eu tentei o código acima, mas substitui UINplitViewController por UINavigationController, mas ele ainda não funciona. Esse método também deve funcionar em um UISplitViewController?
Snips
6
Não é uma boa ideia implementar um método duplicado em uma categoria. Você nunca pode ter certeza de qual implementação será chamada; portanto, na melhor das hipóteses, você pode esperar um comportamento inconsistente. Melhor herdar de UINavigationController e substituir o método em sua classe personalizada.
Sean Woodward
51

Resolvi isso usando o UIModalPresentationPageSheetestilo de apresentação e redimensionando-o imediatamente após a apresentação. Igual a:

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
fonte
Hmmm ... isso não está certo ... redimensionar faz com que o modal seja engraçado ... é como se o conteúdo fosse pressionado para caber na nova caixa de tamanho ou algo assim ... tudo parece engraçado. :(
toofah
Também há problemas de rotação com este ... se você girar enquanto este modal estiver
ativo
2
toofah, editei o código para lidar com o problema de encolhimento / crescimento ao girar; apenas uma questão de fornecer à superview uma margem superior e inferior flexível. Não tenho certeza se estou vendo o outro comportamento.
azdev
1
isso funciona apenas contanto que você não coloque outra visão em cima dela. Como quando você fecha a exibição empurrada acima da exibição apresentada UIModalPresentationPageSheet, ela volta ao tamanho original.
V1ru8
Funcionou. Mas a palavra na exibição parece um pouco borrada. Não sei porque.
Jswang
1

Se você alternar para uma exibição modal diferente, poderá fazer com que o teclado desapareça. Não é bonito e não anima, mas você pode fazer com que ele desapareça.

Seria ótimo se houvesse uma correção, mas por enquanto isso funciona. Você pode inseri-lo em uma categoria UIViewControllere chamá-lo quando quiser que o teclado seja desativado:

@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

Tenha cuidado com isso, enquanto visualizaDidAppear / viewDidDisappear e todos esses métodos são chamados. Como eu disse, não é bonito, mas funciona.

-Adão

Adão
fonte
1

Você também pode contornar isso em um aplicativo universal, basta verificar o idioma e, se for um iPad, não abra o teclado automaticamente e deixe o usuário tocar no que quiser editar.

Pode não ser a solução mais agradável, mas é muito simples e não precisa de hacks sofisticados que irão romper com o próximo grande lançamento do iOS :)

Maciej Swic
fonte
1

Coloque esse código em sua viewWillDisappear: o método do controlador atual é outra maneira de corrigir isso:

Class UIKeyboardImpl = NSClassFromString(@"UIKeyboardImpl");
id activeInstance = [UIKeyboardImpl performSelector:@selector(activeInstance)];
[activeInstance performSelector:@selector(dismissKeyboard)];
História
fonte
1

Descobri que disablesAutomaticKeyboardDismissaladicionar uma disablesAutomaticKeyboardDismissalfunção não funcionava para o meuUITextField em uma caixa de diálogo modal.

O teclado na tela simplesmente não desapareceu.

Minha solução foi desativar todos os controles de entrada de texto na minha caixa de diálogo e reativar os relevantes uma fração de segundo depois.

Parece que quando iOS vê que nenhum dos UITextFieldcontroles são ativados, então ele não se livrar do teclado.

Mike Gledhill
fonte
0

Tenho certeza que você analisou isso, mas você tem certeza de que sua classe de controlador está conectada corretamente como delegada UITextField, certo?

Neal L
fonte
Eu o defino manualmente, e os métodos delegados são chamados, então sim.
Kalle
0

talvez não retorne NÃO, mas SIM. Então isso pode ir embora.

E você também tem um textFieldShouldEndEditingretorno SIM?

E por que você está atirando [nextResponder becomeFirstResponder]?! desculpe eu vejo agora

Eu também tenho vários UITextViews, todos com sua propriedade "editável" definida como FALSE.

Podemos assumir que nenhum deles, por acaso, tem um tagvalor de secondField.tag+1? Nesse caso, você está dizendo para eles se tornarem o primeiro a responder, em vez de renunciar ao primeiro. Talvez coloque algum NSLog () nessa estrutura if.

mvds
fonte
1
NÃO = não insira nova linha, pelo que sei. E defini-lo como SIM não o corrigiu.
Kalle
1
Um UITextField, sendo uma linha por definição, não faz muito com novas linhas, eu acho. Portanto, trata-se mais do processamento da pressão do botão Retornar / Concluído, conforme indicado nos documentos.
Mvds
Tem certeza de que conectou tudo da maneira certa? Você colocou um NSLog("tf %x / method ...",textField);em todas as funções de delegado?
Mvds
Bem, as funções de delegado são chamadas apropriadamente, e não seriam se o delegado não estivesse configurado adequadamente. E o NSLog fornece um EXC_BAD_ACCESS. Também me avisa sobre ser do tipo incompatível no XCode.
Kalle
D'oh. Desculpe, eu deveria ter visto isso. Eu atualizei a resposta acima com os resultados destes NSLogs desde a formatação irá gook-se em comm ..
Kalle
0

Para aqueles que têm problemas com o UINavigationController, consulte minha resposta para uma pergunta semelhante aqui: https://stackoverflow.com/a/10507689/321785

Edit: Eu considero isso uma melhoria na solução de Miha Hribar (já que a decisão está ocorrendo onde deveria), e em oposição ao comentário de Pascal sobre uma categoria no UIViewController

Chris Trahey
fonte
0

pode não ser uma solução perfeita, mas funciona
[self.view endEditing: YES];
de onde seu botão ou gesto é implementado para apresentar modal

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