UITextField: vista de movimiento cuando aparece el teclado

Actualmente estoy trabajando en una aplicación de iPhone con una sola vista, que tiene múltiples UITextFields para la entrada. Cuando el teclado muestra, se superpone a los campos de texto inferiores. Así que agregué el método correspondiente textFieldDidBeginEditing: para mover la vista hacia arriba, que funciona muy bien:

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

Este método verifica, si la fuente del mensaje es uno de los campos de texto que están visibles cuando se muestra el teclado, y si no, mueve la vista hacia arriba.

También agregué el método textFieldDidEndEnditing: que mueve la vista hacia abajo nuevamente (y actualiza algunos objetos del modelo de acuerdo con la entrada 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 } 

Sin embargo, esta solución tiene un defecto simple: cuando termino de editar uno de los campos de texto “ocultos” y toco otro campo de texto, el teclado desaparece, la vista se mueve hacia abajo, la vista se mueve hacia arriba otra vez y el teclado vuelve a aparecer.

¿Hay alguna posibilidad de evitar que el teclado se desvanezca y vuelva a aparecer entre dos ediciones (de los campos de texto “ocultos”) para que la vista solo se mueva cuando el campo de texto seleccionado cambie de uno que estaría oculto por el teclado a uno que no estaría oculto? )?

Esta solución se basa en la de ComSubVie.

Ventajas:

  • Es compatible con la rotación del dispositivo – funciona para todas las orientaciones;
  • No codifica los valores de duración y curva de la animación, los lee desde la notificación del teclado;
  • Utiliza UIKeyboardWillShowNotification lugar de UIKeyboardDidShowNotification para sincronizar animaciones de teclado y acciones personalizadas;
  • No usa el UIKeyboardBoundsUserInfoKey desuso;
  • Maneja el tamaño del teclado debido a presionar la tecla internacional;
  • Se corrigió la pérdida de memoria anulando el registro de eventos del teclado;
  • Todo el código de manejo del teclado está encapsulado en una clase separada: KBKeyboardHandler ;
  • Flexibilidad – KBKeyboardHandler clase KBKeyboardHandler puede ampliarse / modificarse fácilmente para ajustarse mejor a las necesidades específicas;

Limitaciones

  • Funciona para iOS 4 y superior, necesita pequeñas modificaciones para admitir versiones anteriores;
  • Funciona para aplicaciones con una sola UIWindow . Si usa varias UIWindows, puede que necesite modificar el método retrieveFrameFromNotification: .

Uso:

Incluya KBKeyboardHandler.h, KBKeyboardHandler.m y KBKeyboardHandlerDelegate.h en su proyecto. Implemente el protocolo KBKeyboardHandlerDelegate en su controlador de vista: consiste en un único método, que se llamará cuando se muestre el teclado, se ocultará o se cambiará su tamaño. KBKeyboardHandler instancia del KBKeyboardHandler y configura su delegado (típicamente uno mismo). Vea la muestra MyViewController continuación.

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 

Ejemplo MyViewController.h :

 @interface MyViewController : UIViewController ... @end 

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

ACTUALIZACIÓN: se corrigió la advertencia de iOS 7, gracias @weienv.

Acabo de resolver este problema. La solución es una combinación de un observador UIKeyboardDidShowNotification y UIKeyboardDidHideNotification con el texto anteriorFieldDidBeginEditing textFieldDidBeginEditing: y textFieldDidEndEditing: methods.

Necesita tres variables adicionales, una para almacenar el UITextField seleccionado actual (que he denominado activeField), otro para indicar si se ha movido la vista actual y otro para indicar si se muestra el teclado.

Así es como se ven ahora los dos métodos de delegado UITextField :

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

Cuando se carga la vista, se crean los siguientes dos observadores:

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

Y los métodos correspondientes se implementan de la siguiente manera:

 - (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 ahora como se esperaba. El teclado solo se cierra cuando se presiona el botón Hecho, de lo contrario, permanece visible y la vista no se mueve.

Como nota adicional, creo que es posible obtener animationDuration dinámicamente preguntando al objeto NSNotification , ya que ya jugué con una solución similar pero no conseguí que funcionara (lo que hace ahora).

Este controlador de vista debe ser Delegado UITextView y debe establecer self.textview.delegate = self en 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]; } 

Tengo tu problema. Simplemente haz lo más simple, solo da salida a UIScrollview. establecer una propiedad de etiqueta única para cada campo de texto a la vista.

 -(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); }]; } 

Una solución bastante fácil, funciona con todos los tamaños de pantalla

Primero debe incrustar UITextFields en UIScrollView. En mi caso, tuve varios UITextFields y una UITextView.

enter image description here

Luego debe heredar de UITextFieldDelegate, UITextViewDelegate.

class SettingsVC: UIViewController, UITextFieldDelegate, UITextViewDelegate

Asigna los delegates de textfield y textview a uno mismo.

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

Luego usa 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) }