Uikeyboardboundsuserinfokey está preso, o que usar?
-
25-09-2019 - |
Pergunta
Estou trabalhando em um aplicativo para iPad usando o 3.2 SDK. Estou lidando com a obtenção do tamanho do teclado para impedir que meus campos de texto se escondam por trás dele.
Estou recebendo um aviso no Xcode -> Uikeyboardboundsuserinfokey está preso, o que devo usar para não obter esse aviso?
Solução
Eu brinquei com a solução oferecida anteriormente, mas ainda tinha problemas. Aqui está o que eu criei:
- (void)keyboardWillShow:(NSNotification *)aNotification {
[self moveTextViewForKeyboard:aNotification up:YES];
}
- (void)keyboardWillHide:(NSNotification *)aNotification {
[self moveTextViewForKeyboard:aNotification up:NO];
}
- (void) moveTextViewForKeyboard:(NSNotification*)aNotification up: (BOOL) up{
NSDictionary* userInfo = [aNotification userInfo];
// Get animation info from userInfo
NSTimeInterval animationDuration;
UIViewAnimationCurve animationCurve;
CGRect keyboardEndFrame;
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];
// Animate up or down
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:animationDuration];
[UIView setAnimationCurve:animationCurve];
CGRect newFrame = textView.frame;
CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];
newFrame.origin.y -= keyboardFrame.size.height * (up? 1 : -1);
textView.frame = newFrame;
[UIView commitAnimations];
}
Outras dicas
De documentação por UIKeyboardBoundsUserInfoKey
:
A chave para um objeto NSValue que contém um CGrect que identifica o retângulo dos limites do teclado nas coordenadas da janela. Este valor é suficiente para obter o tamanho do teclado. Se você deseja obter a origem do teclado na tela (antes ou depois da animação), use os valores obtidos do Dicionário de Informações do Usuário através das constantes UikeyBoardCenterBeginUserinFokey ou UikeyBoardCenteRendUserinFokey. Use a chave UiKeyBoardFrameBeginUserinFokey ou UiKeyBoardFReEndUserinFokey.
A Apple recomenda a implementação de uma rotina de conveniência como essa (que pode ser implementada como uma adição de categoria a UIScreen
):
+ (CGRect) convertRect:(CGRect)rect toView:(UIView *)view {
UIWindow *window = [view isKindOfClass:[UIWindow class]] ? (UIWindow *) view : [view window];
return [view convertRect:[window convertRect:rect fromWindow:nil] fromView:nil];
}
Para recuperar as propriedades do quadro do teclado ajustadas pela janela.
Eu adotei uma abordagem diferente, que envolve verificar a orientação do dispositivo:
CGRect _keyboardEndFrame;
[[notification.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] getValue:&_keyboardEndFrame];
CGFloat _keyboardHeight = ([[UIDevice currentDevice] orientation] == UIDeviceOrientationPortrait || [[UIDevice currentDevice] orientation] == UIDeviceOrientationPortraitUpsideDown) ? _keyboardEndFrame.size.height : _keyboardEndFrame.size.width;
Você simplesmente usa este código:
//NSVale *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey];
//instead of Upper line we can use either next line or nextest line.
//NSValue *aValue = [info objectForKey:UIKeyboardFrameEndUserInfoKey];
NSValue *aValue = [info objectForKey:UIKeyboardFrameBeginUserInfoKey];
O código a seguir corrige um problema em Resposta de Jay, que assume que UIKeyboardWillShowNotification
Não disparará novamente quando o teclado já estiver presente.
Ao digitar com o teclado japonês/chinês, o iOS dispara um extra UIKeyboardWillShowNotification
com a nova estrutura do teclado, embora o teclado já esteja presente, levando à altura do self.textView
sendo reduzido pela segunda vez no código original.
Isso reduz self.textView
para quase nada. Torna -se então impossível se recuperar desse problema, já que vamos esperar apenas um único UIKeyboardWillHideNotification
Na próxima vez que o teclado for descartado.
Em vez de subtrair/adicionar altura a self.textView
Dependendo se o teclado é mostrado/oculto como no código original, o código a seguir apenas calcula a altura máxima possível para self.textView
Depois de subtrair a altura do teclado na tela.
Isso assume isso self.textView
deve preencher toda a visão do controlador de exibição e não há outra subview que precise ser visível.
- (void)resizeTextViewWithKeyboardNotification:(NSNotification*)notif {
NSDictionary* userInfo = [notif userInfo];
NSTimeInterval animationDuration;
UIViewAnimationCurve animationCurve;
CGRect keyboardFrameInWindowsCoordinates;
[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
[[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameInWindowsCoordinates];
[self resizeTextViewToAccommodateKeyboardFrame:keyboardFrameInWindowsCoordinates
withAnimationDuration:animationDuration
animationCurve:animationCurve];
}
- (void)resizeTextViewToAccommodateKeyboardFrame:(CGRect)keyboardFrameInWindowsCoordinates
withAnimationDuration:(NSTimeInterval)duration
animationCurve:(UIViewAnimationCurve)curve
{
CGRect fullFrame = self.view.frame;
CGRect keyboardFrameInViewCoordinates =
[self.view convertRect:keyboardFrameInWindowsCoordinates fromView:nil];
// Frame of the keyboard that intersects with the view. When keyboard is
// dismissed, the keyboard frame still has width/height, although the origin
// keeps the keyboard out of the screen.
CGRect keyboardFrameVisibleOnScreen =
CGRectIntersection(fullFrame, keyboardFrameInViewCoordinates);
// Max frame availble for text view. Assign it to the full frame first
CGRect newTextViewFrame = fullFrame;
// Deduct the the height of any keyboard that's visible on screen from
// the height of the text view
newTextViewFrame.size.height -= keyboardFrameVisibleOnScreen.size.height;
if (duration)
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:duration];
[UIView setAnimationCurve:curve];
}
// Adjust the size of the text view to the new one
self.textView.frame = newTextViewFrame;
if (duration)
{
[UIView commitAnimations];
}
}
Além disso, não se esqueça de registrar as notificações do teclado no ViewDidload:
- (void)viewDidLoad
{
[super viewDidLoad];
NSNotificationCenter* notifCenter = [NSNotificationCenter defaultCenter];
[notifCenter addObserver:self selector:@selector(resizeTextViewWithKeyboardNotification:) name:UIKeyboardWillShowNotification object:nil];
[notifCenter addObserver:self selector:@selector(resizeTextViewWithKeyboardNotification:) name:UIKeyboardWillHideNotification object:nil];
}
Sobre dividir o código de redimensionamento em duas partes
A razão pela qual o código de redimensionamento do TextView é dividido em duas partes (resizeTextViewWithKeyboardNotification:
e resizeViewToAccommodateKeyboardFrame:withAnimationDuration:animationCurve:
) é corrigir outro problema quando o teclado persistir através de um empurrão de um controlador de vista para outro (veja Como faço para detectar o teclado iOS quando ele permanece entre os controladores?).
Como o teclado já está presente antes de o controlador de exibição ser empurrado, não há notificações adicionais de teclado sendo geradas pelo iOS e, portanto, não há como redimensionar o textView
com base nessas notificações de teclado.
O código acima (assim como o código original) que redimensiona self.textView
Assim funcionará apenas quando o teclado for mostrado depois a vista foi carregada.
Minha solução é criar um singleton que armazena as últimas coordenadas do teclado e em - viewDidAppear:
do ViewController, ligue para:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
// Resize the view if there's any keyboard presence before this
// Only call in viewDidAppear as we are unable to convertRect properly
// before view is shown
[self resizeViewToAccommodateKeyboardFrame:[[UASKeyboard sharedKeyboard] keyboardFrame]
withAnimationDuration:0
animationCurve:0];
}
UASKeyboard
é meu singleton aqui. Idealmente, devemos chamar isso de - viewWillAppear:
, no entanto, na minha experiência (pelo menos no iOS 6), o convertRect:fromView:
método que precisamos usar em resizeViewToAccommodateKeyboardFrame:withAnimationDuration:animationCurve:
Não converte corretamente o quadro do teclado para as coordenadas de exibição antes que a visualização seja totalmente visível.
Basta usar o UIKeyboardFrameBeginUserInfoKey
ou UIKeyboardFrameEndUserInfoKey
chave em vez de UIKeyboardBoundsUserInfoKey
@Jason, você codifica se bem, exceto por um ponto.
No momento, você não está realmente animando nada e a visualização simplesmente 'pop' para o seu novo tamanho.
Você precisa especificar um estado para animar. Uma animação é uma espécie de coisa (do estado)-> (para o estado).
Felizmente, existe um método muito conveniente para especificar o estado atual da visão como o (do estado).
[UIView setAnimationBeginsFromCurrentState:YES];
Se você adicionar essa linha logo após as iniciações: Contexto: Seu código funciona perfeitamente.
- (CGSize)keyboardSize:(NSNotification *)aNotification {
NSDictionary *info = [aNotification userInfo];
NSValue *beginValue = [info objectForKey:UIKeyboardFrameBeginUserInfoKey];
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
CGSize keyboardSize;
if ([UIKeyboardDidShowNotification isEqualToString:[aNotification name]]) {
_screenOrientation = orientation;
if (UIDeviceOrientationIsPortrait(orientation)) {
keyboardSize = [beginValue CGRectValue].size;
} else {
keyboardSize.height = [beginValue CGRectValue].size.width;
keyboardSize.width = [beginValue CGRectValue].size.height;
}
} else if ([UIKeyboardDidHideNotification isEqualToString:[aNotification name]]) {
// We didn't rotate
if (_screenOrientation == orientation) {
if (UIDeviceOrientationIsPortrait(orientation)) {
keyboardSize = [beginValue CGRectValue].size;
} else {
keyboardSize.height = [beginValue CGRectValue].size.width;
keyboardSize.width = [beginValue CGRectValue].size.height;
}
// We rotated
} else if (UIDeviceOrientationIsPortrait(orientation)) {
keyboardSize.height = [beginValue CGRectValue].size.width;
keyboardSize.width = [beginValue CGRectValue].size.height;
} else {
keyboardSize = [beginValue CGRectValue].size;
}
}
return keyboardSize;
}
Aqui está um bom detalhehttp://i-phone-dev.blogspot.com/2012/01/different-way-to-show-keyboard-and.html
Está funcionando assim
Esta é a restrição do botão salvar inferior
@IBOutlet weak var saveBtnBottom: NSLayoutConstraint!
@IBOutlet weak var nameText: UITextField!
Inside ViewDidload
NotificationCenter.default.addObserver(self, selector: #selector(keyBoardWillShow(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyBoardWillHide(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
nameText.delegate = self
Esta é as funções que precisamos
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
nameText.resignFirstResponder()
return true
}
@objc func keyBoardWillShow(notification: Notification){
if let userInfo = notification.userInfo as? Dictionary<String, AnyObject>{
let frame = userInfo[UIResponder.keyboardFrameEndUserInfoKey]
let keyBoardRect = frame?.cgRectValue
if let keyBoardHeight = keyBoardRect?.height {
self.saveBtnBottom.constant = keyBoardHeight
UIView.animate(withDuration: 0.5, animations: {
self.view.layoutIfNeeded()
})
}
}
}
@objc func keyBoardWillHide(notification: Notification){
self.saveBtnBottom.constant = 30.0
UIView.animate(withDuration: 0.5, animations: {
self.view.layoutIfNeeded()
})
}