¿Cómo puedo desplazar el UIScrollView cuando aparece el teclado?

Estoy teniendo problemas con mi código. Estoy tratando de mover el UIScrollView cuando estoy editando un UITextField que debería estar oculto por el teclado emergente.

Estoy moviendo el marco principal en este momento porque no sé cómo ‘desplazarse hacia arriba’ en el código. Así que, hice un poco de código, está funcionando bien, pero cuando edito un UItextfield y cambio a otro UITextField sin presionar el botón ‘volver’, la vista principal se va demasiado arriba.

Hice un NSLog() con mis variables size, distance y textFieldRect.origin.y como puede ver a continuación. Cuando coloco dos UITextField en el mismo lugar (origen y) y hago este ‘cambio’ particular (sin presionar return), obtengo los mismos números, mientras que mi código funcionó bien para la primera edición de UITextField pero no para la segunda edición.

Mira esto:

 - (void)textFieldDidBeginEditing:(UITextField *)textField { { int size; CGRect textFieldRect = [self.view.window convertRect:textField.bounds fromView:textField]; size = textFieldRect.origin.y + textFieldRect.size.height; if (change == FALSE) { size = size - distance; } if (size  PORTRAIT_KEYBOARD_HEIGHT) { distance = size - PORTRAIT_KEYBOARD_HEIGHT + 5; // +5 px for more visibility } NSLog(@"origin %f", textFieldRect.origin.y); NSLog(@"size %d", size); NSLog(@"distance %d", distance); CGRect viewFrame = self.view.frame; viewFrame.origin.y -= distance; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationBeginsFromCurrentState:YES]; [UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION]; [self.view setFrame:viewFrame]; [UIView commitAnimations]; change = FALSE; } - (void)textFieldDidEndEditing:(UITextField *)textField { change = TRUE; CGRect viewFrame = self.view.frame; viewFrame.origin.y += distance; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationBeginsFromCurrentState:YES]; [UIView setAnimationDuration:KEYBOARD_ANIMATION_DURATION]; [self.view setFrame:viewFrame]; [UIView commitAnimations]; } 

Algunas ideas ?

La forma recomendada por Apple es cambiar el contentInset del contentInset UIScrollView . Es una solución muy elegante, porque no tiene que meterse con contentSize . El siguiente código se copia de la Guía de progtwigción del teclado , donde se explica el manejo de este problema. Deberías echarle un vistazo.

 // Call this method somewhere in your view controller setup code. - (void)registerForKeyboardNotifications { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil]; } // Called when the UIKeyboardDidShowNotification is sent. - (void)keyboardWasShown:(NSNotification*)aNotification { NSDictionary* info = [aNotification userInfo]; CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0); scrollView.contentInset = contentInsets; scrollView.scrollIndicatorInsets = contentInsets; // If active text field is hidden by keyboard, scroll it so it's visible // Your application might not need or want this behavior. CGRect aRect = self.view.frame; aRect.size.height -= kbSize.height; if (!CGRectContainsPoint(aRect, activeField.frame.origin) ) { CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y-kbSize.height); [scrollView setContentOffset:scrollPoint animated:YES]; } } // Called when the UIKeyboardWillHideNotification is sent - (void)keyboardWillBeHidden:(NSNotification*)aNotification { UIEdgeInsets contentInsets = UIEdgeInsetsZero; scrollView.contentInset = contentInsets; scrollView.scrollIndicatorInsets = contentInsets; } 

Acabo de implementar esto con Swift 2.0 para iOS9 en Xcode 7 (beta 6), funciona bien aquí.

 override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) registerKeyboardNotifications() } func registerKeyboardNotifications() { NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil) } deinit { NSNotificationCenter.defaultCenter().removeObserver(self) } func keyboardWillShow(notification: NSNotification) { let userInfo: NSDictionary = notification.userInfo! let keyboardSize = userInfo.objectForKey(UIKeyboardFrameBeginUserInfoKey)!.CGRectValue.size let contentInsets = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0) scrollView.contentInset = contentInsets scrollView.scrollIndicatorInsets = contentInsets var viewRect = view.frame viewRect.size.height -= keyboardSize.height if CGRectContainsPoint(viewRect, textField.frame.origin) { let scrollPoint = CGPointMake(0, textField.frame.origin.y - keyboardSize.height) scrollView.setContentOffset(scrollPoint, animated: true) } } func keyboardWillHide(notification: NSNotification) { scrollView.contentInset = UIEdgeInsetsZero scrollView.scrollIndicatorInsets = UIEdgeInsetsZero } 

Editado para Swift 3

Parece que solo necesitas configurar el contentInset y scrollIndicatorInset con Swift 3, el scroll / contentOffset se realiza automáticamente.

 override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) registerKeyboardNotifications() } func registerKeyboardNotifications() { NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil) } deinit { NotificationCenter.default.removeObserver(self) } func keyboardWillShow(notification: NSNotification) { let userInfo: NSDictionary = notification.userInfo! as NSDictionary let keyboardInfo = userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue let keyboardSize = keyboardInfo.cgRectValue.size let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0) scrollView.contentInset = contentInsets scrollView.scrollIndicatorInsets = contentInsets } func keyboardWillHide(notification: NSNotification) { scrollView.contentInset = .zero scrollView.scrollIndicatorInsets = .zero } 

Todas las respuestas aquí parecen olvidarse de las posibilidades del paisaje. Si desea que esto funcione cuando el dispositivo se gira a una vista horizontal, entonces tendrá problemas.

El truco aquí es que, aunque la vista es consciente de la orientación, el teclado no lo es. Esto significa que en Landscape, el ancho de los teclados es en realidad su altura y viceversa.

Para modificar la forma recomendada por Apple de cambiar las inserciones de contenido y obtener su orientación horizontal de soporte, recomendaría usar lo siguiente:

 // Call this method somewhere in your view controller setup code. - (void)registerForKeyboardNotifications { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil]; } // Called when the UIKeyboardDidShowNotification is sent. - (void)keyboardWasShown:(NSNotification*)aNotification { UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; CGSize keyboardSize = [[[notif userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; if (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight ) { CGSize origKeySize = keyboardSize; keyboardSize.height = origKeySize.width; keyboardSize.width = origKeySize.height; } UIEdgeInsets contentInsets = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0); scroller.contentInset = contentInsets; scroller.scrollIndicatorInsets = contentInsets; // If active text field is hidden by keyboard, scroll it so it's visible // Your application might not need or want this behavior. CGRect rect = scroller.frame; rect.size.height -= keyboardSize.height; NSLog(@"Rect Size Height: %f", rect.size.height); if (!CGRectContainsPoint(rect, activeField.frame.origin)) { CGPoint point = CGPointMake(0, activeField.frame.origin.y - keyboardSize.height); NSLog(@"Point Height: %f", point.y); [scroller setContentOffset:point animated:YES]; } } // Called when the UIKeyboardWillHideNotification is sent - (void)keyboardWillBeHidden:(NSNotification*)aNotification { UIEdgeInsets contentInsets = UIEdgeInsetsZero; scrollView.contentInset = contentInsets; scrollView.scrollIndicatorInsets = contentInsets; } 

La parte a tener en cuenta aquí es la siguiente:

 UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; CGSize keyboardSize = [[[notif userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; if (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight ) { CGSize origKeySize = keyboardSize; keyboardSize.height = origKeySize.width; keyboardSize.width = origKeySize.height; } 

Lo que sí es, detecta en qué orientación se encuentra el dispositivo. Si es horizontal, ‘intercambiará’ los valores de ancho y alto de la variable del tamaño del teclado para asegurarse de que se usan los valores correctos en cada orientación.

Para esto no es necesario codificar mucho, es muy fácil como el siguiente código:

todos los textos se muestran en UIScrollview from nib como esta imagen: –

enter image description here

YourViewController.h

 @interface cntrInquiryViewController : UIViewController { IBOutlet UITextField *txtName; IBOutlet UITextField *txtEmail; IBOutlet UIScrollView *srcScrollView; } @end 

conecte IBOutlet de la punta y también conecte a cada delegado de UItextfiled y delegado de scrollview de NIB

 -(void)viewWillAppear:(BOOL)animated { srcScrollView.contentSize = CGSizeMake(320, 500); [super viewWillAppear:YES]; } -(void)textFieldDidBeginEditing:(FMTextField *)textField { [srcScrollView setContentOffset:CGPointMake(0,textField.center.y-140) animated:YES];//you can set your y cordinate as your req also } -(BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; [srcScrollView setContentOffset:CGPointMake(0,0) animated:YES]; return YES; } 

NOTA: si el delegado archivado por texto no está conectado, entonces no funciona ningún método, asegúrese de que todo iBOulate y delegate estén conectados correctamente.

Recomendación de Apple recodificada en Swift + Uso de UIScrollView con diseño automático en iOS (basándose en los siguientes enlaces: enlace 1 , enlace 2 , enlace 3 ):

 import UIKit class ViewController: UIViewController, UITextFieldDelegate { @IBOutlet var t1: UITextField! @IBOutlet var t2: UITextField! @IBOutlet var t3: UITextField! @IBOutlet var t4: UITextField! @IBOutlet var srcScrollView: UIScrollView! @IBOutlet var contentView: UIView! var contentViewCoordinates: CGPoint! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. /* Constraints on content view */ let leftConstraint = NSLayoutConstraint(item:self.contentView, attribute:NSLayoutAttribute.Leading, relatedBy:NSLayoutRelation.Equal, toItem:self.view, attribute:NSLayoutAttribute.Left, multiplier:1.0, constant:0) self.view.addConstraint(leftConstraint) let rightConstraint = NSLayoutConstraint(item:self.contentView, attribute:NSLayoutAttribute.Trailing, relatedBy:NSLayoutRelation.Equal, toItem:self.view, attribute:NSLayoutAttribute.Right, multiplier:1.0, constant:0) self.view.addConstraint(rightConstraint) /* Tap gesture */ let tapGesture: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "hideKeyboard") // prevents the scroll view from swallowing up the touch event of child buttons tapGesture.cancelsTouchesInView = false srcScrollView.addGestureRecognizer(tapGesture) /* Save content view coordinates */ contentViewCoordinates = contentView.frame.origin } func hideKeyboard() { t1.resignFirstResponder() t2.resignFirstResponder() t3.resignFirstResponder() t4.resignFirstResponder() } var activeField: UITextField? func textFieldDidBeginEditing(textField: UITextField) { activeField = textField } func textFieldDidEndEditing(textField: UITextField) { activeField = nil } override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) let center = NSNotificationCenter.defaultCenter() center.addObserver(self, selector: "keyboardOnScreen:", name: UIKeyboardDidShowNotification, object: nil) center.addObserver(self, selector: "keyboardOffScreen:", name: UIKeyboardDidHideNotification, object: nil) } func keyboardOnScreen(notification: NSNotification){ // Retrieve the size and top margin (inset is the fancy word used by Apple) // of the keyboard displayed. let info: NSDictionary = notification.userInfo! let kbSize = info.valueForKey(UIKeyboardFrameEndUserInfoKey)?.CGRectValue().size let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize!.height, 0.0) srcScrollView.contentInset = contentInsets srcScrollView.scrollIndicatorInsets = contentInsets var aRect: CGRect = self.view.frame aRect.size.height -= kbSize!.height //you may not need to scroll, see if the active field is already visible if (CGRectContainsPoint(aRect, activeField!.frame.origin) == false) { let scrollPoint:CGPoint = CGPointMake(0.0, activeField!.frame.origin.y - kbSize!.height) srcScrollView.setContentOffset(scrollPoint, animated: true) } } // func keyboardOnScreen(aNotification: NSNotification) { // let info: NSDictionary = aNotification.userInfo! // let kbSize = info.valueForKey(UIKeyboardFrameEndUserInfoKey)?.CGRectValue().size // // var bkgndRect: CGRect! = activeField?.superview?.frame // // bkgndRect.size.height += kbSize!.height // // activeField?.superview?.frame = bkgndRect // // srcScrollView.setContentOffset(CGPointMake(0.0, activeField!.frame.origin.y - kbSize!.height), animated: true) // } func keyboardOffScreen(notification: NSNotification){ let contentInsets:UIEdgeInsets = UIEdgeInsetsZero srcScrollView.contentInset = contentInsets srcScrollView.scrollIndicatorInsets = contentInsets self.srcScrollView.setContentOffset(CGPointMake(0, -self.view.frame.origin.y/2), animated: true) } } 

Lo único que actualizaría en el código de Apple es el método keyboardWillBeHidden: para proporcionar una transición fluida.

 // Called when the UIKeyboardWillHideNotification is sent - (void)keyboardWillBeHidden:(NSNotification*)aNotification { UIEdgeInsets contentInsets = UIEdgeInsetsZero; [UIView animateWithDuration:0.4 animations:^{ self.scrollView.contentInset = contentInsets; }]; self.scrollView.scrollIndicatorInsets = contentInsets; } 

Aquí hay una respuesta compatible con Swift 3 , que también funcionará con controladores de vista dentro de un controlador de navegación, ya que cambiarán la propiedad contentInset.top .

 override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.registerKeyboardNotifications() } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) self.unregisterKeyboardNotifications() } func registerKeyboardNotifications() { NotificationCenter.default.addObserver(self, selector: #selector(LoginViewController.keyboardDidShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(LoginViewController.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil) } func unregisterKeyboardNotifications() { NotificationCenter.default.removeObserver(self) } func keyboardDidShow(notification: NSNotification) { let userInfo: NSDictionary = notification.userInfo! as NSDictionary let keyboardInfo = userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue let keyboardSize = keyboardInfo.cgRectValue.size // Get the existing contentInset for the scrollView and set the bottom property to be the height of the keyboard var contentInset = self.scrollView.contentInset contentInset.bottom = keyboardSize.height self.scrollView.contentInset = contentInset self.scrollView.scrollIndicatorInsets = contentInset } func keyboardWillHide(notification: NSNotification) { var contentInset = self.scrollView.contentInset contentInset.bottom = 0 self.scrollView.contentInset = contentInset self.scrollView.scrollIndicatorInsets = UIEdgeInsets.zero } 

Solución Swift 4 :

 override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) registerKeyboardNotifications() } func registerKeyboardNotifications() { NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil) } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self) } @objc func keyboardWillShow(notification: NSNotification) { let userInfo: NSDictionary = notification.userInfo! as NSDictionary let keyboardInfo = userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue let keyboardSize = keyboardInfo.cgRectValue.size let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0) scrollView.contentInset = contentInsets scrollView.scrollIndicatorInsets = contentInsets } @objc func keyboardWillHide(notification: NSNotification) { scrollView.contentInset = .zero scrollView.scrollIndicatorInsets = .zero } 

He encontrado que las respuestas anteriores han quedado obsoletas. También no es perfecto cuando se desplaza.

Aquí hay una versión rápida.

Se desplazará justo debajo del campo de texto, sin espacio adicional. Y restaurará a la manera en que fue como su primera aparición.

 //add observer override func viewDidLoad() { super.viewDidLoad() NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ARVHttpPlayVC.keyboardDidShow(_:)), name: UIKeyboardDidShowNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ARVHttpPlayVC.keyboardDidHide(_:)), name: UIKeyboardDidHideNotification, object: nil) } func keyboardDidShow(notification: NSNotification) { let userInfo: NSDictionary = notification.userInfo! let keyboardSize = userInfo.objectForKey(UIKeyboardFrameEndUserInfoKey)!.CGRectValue.size let difference = keyboardSize.height - (self.view.frame.height - inputTextField.frame.origin.y - inputTextField.frame.size.height) if difference > 0 { var contentInset:UIEdgeInsets = self.scrollView.contentInset contentInset.bottom = difference self.scrollView.contentInset = contentInset let scrollPoint = CGPointMake(0, difference) self.scrollView.setContentOffset(scrollPoint, animated: true) } } func keyboardDidHide(notification: NSNotification) { let contentInset:UIEdgeInsets = UIEdgeInsetsZero self.scrollView.contentInset = contentInset } //remove observer deinit { NSNotificationCenter.defaultCenter().removeObserver(self) } 

Esto es lo que he estado usando. Es simple y funciona bien.

 #pragma mark - Scrolling -(void)scrollElement:(UIView *)view toPoint:(float)y { CGRect theFrame = view.frame; float orig_y = theFrame.origin.y; float diff = y - orig_y; if (diff < 0) [self scrollToY:diff]; else [self scrollToY:0]; } -(void)scrollToY:(float)y { [UIView animateWithDuration:0.3f animations:^{ [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; self.view.transform = CGAffineTransformMakeTranslation(0, y); }]; } 

Use la UITextField delegado textFieldDidBeginEditing: para cambiar su vista hacia arriba, y también agregue un observador de notificación para regresar la vista a la normalidad cuando el teclado se esconde:

 -(void)textFieldDidBeginEditing:(UITextField *)textField { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; if (self.view.frame.origin.y == 0) [self scrollToY:-90.0]; // y can be changed to your liking } -(void)keyboardWillHide:(NSNotification*)note { [self scrollToY:0]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } 

Este es el código final con mejoras en Swift

  //MARK: UITextFieldDelegate func textFieldDidBeginEditing(textField: UITextField!) { //delegate method self.textField = textField } func textFieldShouldReturn(textField: UITextField!) -> Bool { //delegate method textField.resignFirstResponder() return true } //MARK: Keyboard handling override func viewWillDisappear(animated: Bool) { super.viewWillDisappear(animated) unregisterKeyboardNotifications() } func registerKeyboardNotifications() { NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(UCProfileSettingsViewController.keyboardDidShow(_:)), name: UIKeyboardDidShowNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(UCProfileSettingsViewController.keyboardWillHide(_:)), name: UIKeyboardWillHideNotification, object: nil) } func unregisterKeyboardNotifications() { NSNotificationCenter.defaultCenter().removeObserver(self) } func keyboardDidShow(notification: NSNotification) { let userInfo: NSDictionary = notification.userInfo! let keyboardSize = userInfo.objectForKey(UIKeyboardFrameBeginUserInfoKey)!.CGRectValue.size let contentInsets = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0) scrollView.contentInset = contentInsets scrollView.scrollIndicatorInsets = contentInsets var viewRect = self.view.frame viewRect.size.height -= keyboardSize.height let relativeFieldFrame: CGRect = textField.convertRect(textField.frame, toView: self.view) if CGRectContainsPoint(viewRect, relativeFieldFrame.origin) { let scrollPoint = CGPointMake(0, relativeFieldFrame.origin.y - keyboardSize.height) scrollView.setContentOffset(scrollPoint, animated: true) } } func keyboardWillHide(notification: NSNotification) { scrollView.contentInset = UIEdgeInsetsZero scrollView.scrollIndicatorInsets = UIEdgeInsetsZero } 

Pruebe este código en Swift 3:

 override func viewDidAppear(_ animated: Bool) { setupViewResizerOnKeyboardShown() } func setupViewResizerOnKeyboardShown() { NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShowForResizing), name: Notification.Name.UIKeyboardWillShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHideForResizing), name: Notification.Name.UIKeyboardWillHide, object: nil) } func keyboardWillShowForResizing(notification: Notification) { if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue, let window = self.view.window?.frame { // We're not just minusing the kb height from the view height because // the view could already have been resized for the keyboard before self.view.frame = CGRect(x: self.view.frame.origin.x, y: self.view.frame.origin.y, width: self.view.frame.width, height: window.origin.y + window.height - keyboardSize.height) } else { debugPrint("We're showing the keyboard and either the keyboard size or window is nil: panic widely.") } } func keyboardWillHideForResizing(notification: Notification) { if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue { let viewHeight = self.view.frame.height self.view.frame = CGRect(x: self.view.frame.origin.x, y: self.view.frame.origin.y, width: self.view.frame.width, height: viewHeight) //viewHeight + keyboardSize.height } else { debugPrint("We're about to hide the keyboard and the keyboard size is nil. Now is the rapture.") } } deinit { NotificationCenter.default.removeObserver(self) } 

Yo haría eso así. Es una gran cantidad de código, pero asegura que el campo de texto actualmente en foco se centra verticalmente en el ‘espacio disponible’:

 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [[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 { [super viewWillDisappear:animated]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } - (void)keyboardWillShow:(NSNotification *)notification { NSDictionary *info = [notification userInfo]; NSValue *keyBoardEndFrame = [info objectForKey:UIKeyboardFrameEndUserInfoKey]; CGSize keyboardSize = [keyBoardEndFrame CGRectValue].size; self.keyboardSize = keyboardSize; [self adjustScrollViewOffsetToCenterTextField:self.currentTextField]; } - (void)keyboardWillHide:(NSNotification *)notification { self.keyboardSize = CGSizeZero; } - (IBAction)textFieldGotFocus:(UITextField *)sender { sender.inputAccessoryView = self.keyboardAccessoryView; self.currentTextField = sender; [self adjustScrollViewOffsetToCenterTextField:sender]; } - (void)adjustScrollViewOffsetToCenterTextField:(UITextField *)textField { CGRect textFieldFrame = textField.frame; float keyboardHeight = MIN(self.keyboardSize.width, self.keyboardSize.height); float visibleScrollViewHeight = self.scrollView.frame.size.height - keyboardHeight; float offsetInScrollViewCoords = (visibleScrollViewHeight / 2) - (textFieldFrame.size.height / 2); float scrollViewOffset = textFieldFrame.origin.y - offsetInScrollViewCoords; [UIView animateWithDuration:.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{ self.scrollView.contentOffset = CGPointMake(self.scrollView.contentOffset.x, scrollViewOffset); }completion:NULL]; } you'll need these two properties in your @interface... @property (nonatomic, assign) CGSize keyboardSize; @property (nonatomic, strong) UITextField *currentTextField; 

Tenga en cuenta que la acción - (IBAction)textFieldGotFocus: se conecta al estado DidBeginEditing cada textField.

También sería un poco mejor obtener la duración de la animación de la notificación del teclado y usarla para la animación scrollview en lugar de un valor fijo, pero demandarme, esto fue lo suficientemente bueno para mí;)

En realidad, no necesita un UIScrollView para hacer esto. Usé este código y me funciona:

 -(BOOL)textFieldShouldBeginEditing:(UITextField *)textField { if (textField==_myTextField) { [self keyBoardAppeared]; } return true; } -(void)textFieldDidEndEditing:(UITextField *)textField { if (textField==_myTextField) { [self keyBoardDisappeared]; } } -(void) keyBoardAppeared { CGRect frame = self.view.frame; [UIView animateWithDuration:0.3 delay:0 options: UIViewAnimationCurveEaseOut animations:^{ self.view.frame = CGRectMake(frame.origin.x, frame.origin.y-215, frame.size.width, frame.size.height); } completion:^(BOOL finished){ }]; } -(void) keyBoardDisappeared { CGRect frame = self.view.frame; [UIView animateWithDuration:0.3 delay:0 options: UIViewAnimationCurveEaseOut animations:^{ self.view.frame = CGRectMake(frame.origin.x, frame.origin.y+215, frame.size.width, frame.size.height); } completion:^(BOOL finished){ }]; } 

Puede desplazarse utilizando la propiedad contentOffset en UIScrollView , por ejemplo,

 CGPoint offset = scrollview.contentOffset; offset.y -= KEYBOARD_HEIGHT + 5; scrollview.contentOffset = offset; 

También hay un método para hacer scroll animado.

En cuanto a la razón por la cual su segunda edición no se desplaza correctamente, podría ser porque parece asumir que aparecerá un nuevo teclado cada vez que se inicie la edición. Podría intentar verificar si ya ha ajustado la posición visible del “teclado” (y también verificar la visibilidad del teclado en el momento antes de revertirlo).

Una mejor solución podría ser escuchar la notificación del teclado, por ejemplo:

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; 

Sé que es una vieja pregunta ahora, pero pensé que podría ayudar a otros. Quería algo un poco más fácil de implementar para algunas aplicaciones que tenía, así que hice una clase para esto. Puede descargarlo aquí si lo desea: https://github.com/sdernley/iOSTextFieldHandler

Es tan simple como configurar todos los UITextFields para tener un delegado de sí mismo

 textfieldname.delegate = self; 

Y luego agregar esto a su controlador de vista con el nombre de su scrollView y botón de enviar

 - (void)textFieldDidBeginEditing:(UITextField *)textField { [iOSTextFieldHandler TextboxKeyboardMover:containingScrollView tf:textField btn:btnSubmit]; } 

La siguiente es mi solución que funciona (5 pasos)

Paso 1: Agregue un observador para capturar qué UITEXTFIELD o UITEXTVIEW ShoudBeginEditing (donde se inited object o ViewDidLoad.

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateActiveField:) name:@"UPDATE_ACTIVE_FIELD" object:nil]; 

Paso 2: publique una notificación cuando ..Debería comenzar a editar con OBJETO de UITEXTFIELD o UITEXTVIEW

 -(BOOL)textViewShouldBeginEditing:(UITextView *)textView { [[NSNotificationCenter defaultCenter] postNotificationName:@"UPDATE_ACTIVE_FIELD" object:textView]; return YES; } 

Paso 3: El método que (Step1 calles) asigna el actual UITEXTFIELD o UITEXTVIEW

 -(void) updateActiveField: (id) sender { activeField = [sender object]; } 

Paso 4: agregue el observador de teclado UIKeyboardWillShowNotification (el mismo lugar que Step1)

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil]; 

y método:

 // Called when the UIKeyboardDidShowNotification is sent. - (void)keyboardWasShown:(NSNotification*)aNotification { NSDictionary* info = [aNotification userInfo]; CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0); _currentEdgeInsets = self.layoutPanel.contentInset; // store current insets to restre them later self.layoutPanel.contentInset = contentInsets; self.layoutPanel.scrollIndicatorInsets = contentInsets; // If active text field is hidden by keyboard, scroll it so it's visible CGRect aRect = self.view.frame; aRect.size.height -= kbSize.height; UIWindow *window = [[UIApplication sharedApplication] keyWindow]; CGPoint p = [activeField convertPoint:activeField.bounds.origin toView:window]; if (!CGRectContainsPoint(aRect, p) ) { CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y +kbSize.height); [self.layoutPanel setContentOffset:scrollPoint animated:YES]; self.layoutPanel.scrollEnabled = NO; } } 

Paso 5: agregue el observador de teclado UIKeyboardWillHideNotification (el mismo lugar que el paso 1)

  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil]; 

y método:

 // Called when the UIKeyboardWillHideNotification is sent - (void)keyboardWillBeHidden:(NSNotification*)aNotification { self.layoutPanel.contentInset = _currentEdgeInsets; self.layoutPanel.scrollIndicatorInsets = _currentEdgeInsets; self.layoutPanel.scrollEnabled = YES; } 

¡Recuerda eliminar observadores!

Use la siguiente extensión si no desea calcular demasiado:

 func scrollSubviewToBeVisible(subview: UIView, animated: Bool) { let visibleFrame = UIEdgeInsetsInsetRect(self.bounds, self.contentInset) let subviewFrame = subview.convertRect(subview.bounds, toView: self) if (!CGRectContainsRect(visibleFrame, subviewFrame)) { self.scrollRectToVisible(subviewFrame, animated: animated) } } 

Y tal vez quieras mantener tu UITextField siempre visible:

 func textViewDidChange(textView: UITextView) { self.scrollView?.scrollSubviewToBeVisible(textView, animated: false) } 

Utilicé esta respuesta proporcionada por Sudheer Palchuri https://stackoverflow.com/users/2873919/sudheer-palchuri https://stackoverflow.com/a/32583809/6193496

En ViewDidLoad, registre las notificaciones:

 NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(DetailsViewController.keyboardWillShow(_:)), name:UIKeyboardWillShowNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(DetailsViewController.keyboardWillHide(_:)), name:UIKeyboardWillHideNotification, object: nil) 

Agregue a continuación métodos de observador que hacen el desplazamiento automático cuando aparece el teclado.

 func textFieldShouldReturn(textField: UITextField) -> Bool { textField.resignFirstResponder() return true } func keyboardWillShow(notification:NSNotification){ var userInfo = notification.userInfo! var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).CGRectValue() keyboardFrame = self.view.convertRect(keyboardFrame, fromView: nil) var contentInset:UIEdgeInsets = self.scrollView.contentInset contentInset.bottom = keyboardFrame.size.height self.scrollView.contentInset = contentInset } func keyboardWillHide(notification:NSNotification){ var contentInset:UIEdgeInsets = UIEdgeInsetsZero self.scrollView.contentInset = contentInset } 

Mi solución tiene 4 pasos:
– Paso 1: la función escucha cuando aparece el teclado

 - (void)keyboardWasShown:(NSNotification *)notification { // Get the size of the keyboard. CGSize keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; //top: 64 for navigation bar, 0 for without navigation UIEdgeInsets contentInsets = UIEdgeInsetsMake(64, 0, keyboardSize.height, 0); _scrollView.contentInset = contentInsets; _scrollView.scrollIndicatorInsets = contentInsets; } 

– Paso 2: la función escucha cuando el teclado desaparece

 - (void)keyboardWillHide:(NSNotification *)notification { //top: 64 for navigatiob bar UIEdgeInsets contentInsets = UIEdgeInsetsMake(64, 0, 0, 0); [_editScrollView setContentInset: contentInsets]; [_editScrollView setScrollIndicatorInsets: contentInsets]; } 

– Paso 3: agregue estas funciones al centro de notificaciones:

 - (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; } 

– Paso 4: eliminar la escucha cuando el controlador de vista se disipa

 - (void)viewDidDisappear:(BOOL)animated{ [super viewDidDisappear:animated]; [[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter]removeObserver:self name:UIKeyboardWillHideNotification object:nil]; } 

Solución Swift 4.2 que tiene en cuenta las posibles alturas de UIToolbar y UITabBar.

 private func setupKeyboardNotifications() { NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIControl.keyboardWillShowNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIControl.keyboardWillHideNotification, object: nil) } @objc func keyboardWillShow(_ notification: Notification) { let userInfo: NSDictionary = notification.userInfo! as NSDictionary let keyboardSize = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue.size let tabbarHeight = tabBarController?.tabBar.frame.size.height ?? 0 let toolbarHeight = navigationController?.toolbar.frame.size.height ?? 0 let bottomInset = keyboardSize.height - tabbarHeight - toolbarHeight scrollView.contentInset.bottom = bottomInset scrollView.scrollIndicatorInsets.bottom = bottomInset } @objc func keyboardWillHide(_ notification: Notification) { scrollView.contentInset = .zero scrollView.scrollIndicatorInsets = .zero } 

And of course, you have to unregister the observer at some point.