UITextField: move view quando o teclado aparece

Atualmente estou trabalhando em um aplicativo para iPhone com uma única visualização, que possui vários UITextFields para input. Quando o teclado mostra, ele sobrepõe os campos de texto inferiores. Então, adicionei o método textFieldDidBeginEditing: correspondente, para mover a visualização para cima, o que funciona muito bem:

 - (void)textFieldDidBeginEditing:(UITextField *)textField { if ( ( textField != inputAmount ) && ( textField != inputAge ) ) { NSTimeInterval animationDuration = 0.300000011920929; CGRect frame = self.view.frame; frame.origin.y -= kOFFSET_FOR_KEYBOARD; frame.size.height += kOFFSET_FOR_KEYBOARD; [UIView beginAnimations:@"ResizeForKeyboard" context:nil]; [UIView setAnimationDuration:animationDuration]; self.view.frame = frame; [UIView commitAnimations]; } } 

Esse método verifica se a origem da mensagem é um dos campos de texto visíveis quando o teclado é exibido e, caso contrário, move a visualização para cima.

Eu também adicionei o método textFieldDidEndEnditing: , que move a vista para baixo novamente (e atualiza alguns objects do modelo de acordo com a input modificada):

 - (void)textFieldDidEndEditing:(UITextField *)textField { if ( ( textField != inputMenge ) && ( textField != inputAlter ) ) { NSTimeInterval animationDuration = 0.300000011920929; CGRect frame = self.view.frame; frame.origin.y += kOFFSET_FOR_KEYBOARD; frame.size.height -= kOFFSET_FOR_KEYBOARD; [UIView beginAnimations:@"ResizeForKeyboard" context:nil]; [UIView setAnimationDuration:animationDuration]; self.view.frame = frame; [UIView commitAnimations]; } // Additional Code } 

No entanto, essa solução tem uma falha simples: quando termino de editar um dos campos de texto “ocultos” e toco em outro campo de texto, o teclado desaparece, a exibição desce, a vista se move novamente e o teclado reaparece.

Existe alguma possibilidade de impedir que o teclado desapareça e reapareça entre duas edições (dos campos de texto “ocultos” – de modo que a exibição só se move quando o campo de texto selecionado muda de um que seria oculto pelo teclado para um que não estaria oculto? )?

Esta solução é baseada na solução ComSubVie.

Vantagens:

  • Suporta rotação de dispositivos – funciona para todas as orientações;
  • Ele não codifica os valores para a duração e a curva da animação, lê-os a partir da notificação do teclado;
  • Ele utiliza o UIKeyboardWillShowNotification vez do UIKeyboardDidShowNotification para sincronizar a animação de teclado e ações personalizadas;
  • Não usa o UIKeyboardBoundsUserInfoKey obsoleto;
  • Ele lida com o redimensionamento do teclado devido a pressionar a tecla internacional;
  • Corrigido memory leaks ao cancelar o registro de events de teclado;
  • Todo o código de manipulação de teclado é encapsulado em uma class separada – KBKeyboardHandler ;
  • Flexibilidade – KBKeyboardHandler class KBKeyboardHandler pode ser facilmente estendida / modificada para melhor atender às necessidades específicas;

Limitações:

  • Funciona para iOS 4 e acima, ele precisa de pequenas modificações para suportar versões mais antigas;
  • Ele funciona para aplicativos com uma única UIWindow . Se você usar vários UIWindows, talvez seja necessário modificar o método retrieveFrameFromNotification:

Uso:

Inclua KBKeyboardHandler.h, KBKeyboardHandler.m e KBKeyboardHandlerDelegate.h no seu projeto. Implemente o protocolo KBKeyboardHandlerDelegate no seu controlador de visualização – ele consiste em um único método, que será chamado quando o teclado for mostrado, oculto ou seu tamanho for alterado. Instancie o KBKeyboardHandler e defina seu delegado (geralmente próprio). Veja o exemplo MyViewController abaixo.

KBKeyboardHandler.h :

 #import  #import  @protocol KBKeyboardHandlerDelegate; @interface KBKeyboardHandler : NSObject - (id)init; // Put 'weak' instead of 'assign' if you use ARC @property(nonatomic, assign) id delegate; @property(nonatomic) CGRect frame; @end 

KBKeyboardHandler.m :

 #import "KBKeyboardHandler.h" #import "KBKeyboardHandlerDelegate.h" @implementation KBKeyboardHandler - (id)init { self = [super init]; if (self) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; } return self; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; [super dealloc]; } @synthesize delegate; @synthesize frame; - (void)keyboardWillShow:(NSNotification *)notification { CGRect oldFrame = self.frame; [self retrieveFrameFromNotification:notification]; if (oldFrame.size.height != self.frame.size.height) { CGSize delta = CGSizeMake(self.frame.size.width - oldFrame.size.width, self.frame.size.height - oldFrame.size.height); if (self.delegate) [self notifySizeChanged:delta notification:notification]; } } - (void)keyboardWillHide:(NSNotification *)notification { if (self.frame.size.height > 0.0) { [self retrieveFrameFromNotification:notification]; CGSize delta = CGSizeMake(-self.frame.size.width, -self.frame.size.height); if (self.delegate) [self notifySizeChanged:delta notification:notification]; } self.frame = CGRectZero; } - (void)retrieveFrameFromNotification:(NSNotification *)notification { CGRect keyboardRect; [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardRect]; self.frame = [[UIApplication sharedApplication].keyWindow.rootViewController.view convertRect:keyboardRect fromView:nil]; } - (void)notifySizeChanged:(CGSize)delta notification:(NSNotification *)notification { NSDictionary *info = [notification userInfo]; UIViewAnimationOptions curve; [[info objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&curve]; NSTimeInterval duration; [[info objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&duration]; void (^action)(void) = ^{ [self.delegate keyboardSizeChanged:delta]; }; [UIView animateWithDuration:duration delay:0.0 options:curve animations:action completion:nil]; } @end 

KBKeyboardHandlerDelegate.h :

 @protocol KBKeyboardHandlerDelegate - (void)keyboardSizeChanged:(CGSize)delta; @end 

Amostra MyViewController.h :

 @interface MyViewController : UIViewController ... @end 

Amostra MyViewController.m :

 @implementation MyViewController { KBKeyboardHandler *keyboard; } - (void)dealloc { keyboard.delegate = nil; [keyboard release]; [super dealloc]; } - (void)viewDidLoad { [super viewDidLoad]; keyboard = [[KBKeyboardHandler alloc] init]; keyboard.delegate = self; } - (void)viewDidUnload { [super viewDidUnload]; keyboard.delegate = nil; [keyboard release]; keyboard = nil; } - (void)keyboardSizeChanged:(CGSize)delta { // Resize / reposition your views here. All actions performed here // will appear animated. // delta is the difference between the previous size of the keyboard // and the new one. // For instance when the keyboard is shown, // delta may has width=768, height=264, // when the keyboard is hidden: width=-768, height=-264. // Use keyboard.frame.size to get the real keyboard size. // Sample: CGRect frame = self.view.frame; frame.size.height -= delta.height; self.view.frame = frame; } 

ATUALIZAÇÃO: Corrigido o aviso do iOS 7, obrigado @weienv.

Eu apenas resolvi esse problema. A solução é uma combinação de um observador UIKeyboardDidHideNotification e UIKeyboardDidHideNotification com os textFieldDidBeginEditing: e textFieldDidEndEditing: .

Você precisa de três variables ​​adicionais, uma para armazenar o UITextField atual selecionado (que chamei de activeField), uma para indicar se a exibição atual foi movida e outra para indicar se o teclado é exibido.

É assim que os dois methods de delegação UITextField agora:

 - (void)textFieldDidBeginEditing:(UITextField *)textField { activeField = textField; } - (void)textFieldDidEndEditing:(UITextField *)textField { activeField = nil; // Additional Code } 

Quando a exibição é carregada, os dois observadores a seguir são criados:

 - (void)viewDidLoad { // Additional Code [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasHidden:) name:UIKeyboardDidHideNotification object:nil]; } 

E os methods correspondentes são implementados da seguinte forma:

 - (void)keyboardWasShown:(NSNotification *)aNotification { if ( keyboardShown ) return; if ( ( activeField != inputAmount ) && ( activeField != inputAge ) ) { NSDictionary *info = [aNotification userInfo]; NSValue *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey]; CGSize keyboardSize = [aValue CGRectValue].size; NSTimeInterval animationDuration = 0.300000011920929; CGRect frame = self.view.frame; frame.origin.y -= keyboardSize.height-44; frame.size.height += keyboardSize.height-44; [UIView beginAnimations:@"ResizeForKeyboard" context:nil]; [UIView setAnimationDuration:animationDuration]; self.view.frame = frame; [UIView commitAnimations]; viewMoved = YES; } keyboardShown = YES; } - (void)keyboardWasHidden:(NSNotification *)aNotification { if ( viewMoved ) { NSDictionary *info = [aNotification userInfo]; NSValue *aValue = [info objectForKey:UIKeyboardBoundsUserInfoKey]; CGSize keyboardSize = [aValue CGRectValue].size; NSTimeInterval animationDuration = 0.300000011920929; CGRect frame = self.view.frame; frame.origin.y += keyboardSize.height-44; frame.size.height -= keyboardSize.height-44; [UIView beginAnimations:@"ResizeForKeyboard" context:nil]; [UIView setAnimationDuration:animationDuration]; self.view.frame = frame; [UIView commitAnimations]; viewMoved = NO; } keyboardShown = NO; } 

Este código funciona agora como esperado. O teclado só é descartado quando o botão Concluído é pressionado, caso contrário, ele permanece visível e a exibição não é movida.

Como uma nota adicional, eu acho que é possível obter o animationDuration dinamicamente perguntando o object NSNotification , já que eu já joguei com uma solução similar mas não consegui que funcionasse (o que ele faz agora).

Esse view controller deve ser UITextView Delegate e você deve definir self.textview.delegate = self in viewdidload

  -(void) textViewDidBeginEditing:(UITextView *)textView { NSLog(@"%f",self.view.frame.origin.y); [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.25f]; CGRect frame = self.view.frame; frame.origin.y =frame.origin.y -204; [self.view setFrame:frame]; [UIView commitAnimations]; } -(void) textViewDidEndEditing:(UITextView *)textView { [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:0.25f]; CGRect frame = self.view.frame; frame.origin.y = frame.origin.y + 204; [self.view setFrame:frame]; [UIView commitAnimations]; } 

Eu tenho o seu problema, basta fazer uma coisa simples, apenas dar saída ao UIScrollview. defina a propriedade Tag exclusiva para cada campo de texto na visualização.

 -(void)textFieldDidBeginEditing:(UITextField *)textField { switch (textField.tag) { case 2: //can be your textfiled tag { CGPoint scrollPoint = CGPointMake(0, yourtextfield.frame.origin.y-150); //set figure y-150 as per your comfirt [scrollview setContentOffset:scrollPoint animated:YES]; }break; case 3: { CGPoint scrollPoint = CGPointMake(0, yourtextfield.frame.origin.y-180); //set figure y-180 as per your comfirt [scrollview setContentOffset:scrollPoint animated:YES]; }break; ... } } -(void)textFieldDidEndEditing:(UITextField *)textField{ if(textField.tag==3){ [scrollview setContentOffset:CGPointZero animated:YES]; } //set the last textfield when you want to disappear keyboard. } 
 Write below code in your view controller. tbl is your table view. -(void)viewWillAppear:(BOOL)animated{ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; } -(void) viewWillDisappear:(BOOL)animated { [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } #pragma mark - Keyboard Methods -(void)keyboardWillShow:(NSNotification *)notification { // 375 × 667 ( 750 × 1334 ) iPhone 6 //414 × 736 CGRect keyboardRect = [[[notification userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; int H = [[UIScreen mainScreen] bounds].size.height - 64- 20 -keyboardRect.size.height; [UIView animateWithDuration:0.5 animations:^{ tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, H, tbl.contentInset.right); }]; } -(void)keyboardWillChangeFrame:(NSNotification *)notification { CGRect keyboardRect = [[[notification userInfo] valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; // int H = IS_IPHONE_5?504-keyboardRect.size.height:416-keyboardRect.size.height; int H = [[UIScreen mainScreen] bounds].size.height - 64- 20 -keyboardRect.size.height; [UIView animateWithDuration:0.5 animations:^{ // scroll.frame = rect; tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, H, tbl.contentInset.right); }]; } -(void)keyboardWillHide:(NSNotification *)notification { [UIView animateWithDuration:0.3 animations:^{ // scroll.frame = rect; tbl.contentInset = UIEdgeInsetsMake(tbl.contentInset.top, tbl.contentInset.left, 0, tbl.contentInset.right); }]; } 

Solução bastante fácil, funciona com todos os tamanhos de canvas

Primeiro você tem que incorporar você UITextFields a um UIScrollView. No meu caso, eu tinha vários UITextFields e um UITextView.

insira a descrição da imagem aqui

Então você tem que herdar de UITextFieldDelegate, UITextViewDelegate.

class SettingsVC: UIViewController, UITextFieldDelegate, UITextViewDelegate

Atribua os delegates do campo de texto e da visão de texto ao self.

fullNameTextField.delegate = self usernameTextField.delegate = self websiteTextField.delegate = self profileDescription.delegate = self

Então use este código:

 var editingTextInput: UIView! override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardShown(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidShow, object: nil) } func keyboardShown(notification: NSNotification) { if let infoKey = notification.userInfo?[UIKeyboardFrameEndUserInfoKey], let rawFrame = (infoKey as AnyObject).cgRectValue { let keyboardFrame = view.convert(rawFrame, to: view) let editingTextInputFrame = self.editingTextInput.convert(self.editingTextInput.frame, to: view) if editingTextInputFrame.maxY > keyboardFrame.minY{ let diff = keyboardFrame.minY - editingTextInputFrame.maxY containerScrollView.setContentOffset(CGPoint(x: 0, y: -diff), animated: true) } } } func textFieldDidBeginEditing(_ textField: UITextField) { self.editingTextInput = textField } func textViewDidBeginEditing(_ textView: UITextView) { self.editingTextInput = textView } func textFieldDidEndEditing(_ textField: UITextField) { containerScrollView.setContentOffset(CGPoint.zero, animated: true) } func textViewDidEndEditing(_ textView: UITextView) { containerScrollView.setContentOffset(CGPoint.zero, animated: true) }