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?

Foi útil?

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

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()
    })
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top