Hacer un desplazamiento de UITableView cuando se selecciona el campo de text

Después de mucho ensayo y error, me estoy rindiendo y haciendo la pregunta. He visto muchas personas con problemas similares, pero no puedo lograr que todas las respuestas funcionen bien.

Tengo una UITableView que se compone de celdas personalizadas. Las celdas están hechas de 5 campos de text uno al lado del otro (algo así como una cuadrícula).

Cuando trato de desplazarme y editar las celdas en la parte inferior de UITableView , no puedo lograr que mis celdas se UITableView correctamente sobre el keyboard.

He visto muchas respuestas hablando de cambiar el tamaño de las vistas, etc … pero ninguno ha funcionado muy bien hasta ahora.

¿Alguien podría aclarar la forma "correcta" de hacer esto con un ejemplo de código concreto?

Si usa UITableViewController en lugar de UIViewController, lo hará automáticamente.

La function que hace el desplazamiento podría ser mucho más simple:

 - (void) textFieldDidBeginEditing:(UITextField *)textField { UITableViewCell *cell; if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) { // Load resources for iOS 6.1 or earlier cell = (UITableViewCell *) textField.superview.superview; } else { // Load resources for iOS 7 or later cell = (UITableViewCell *) textField.superview.superview.superview; // TextField -> UITableVieCellContentView -> (in iOS 7!)ScrollView -> Cell! } [tView scrollToRowAtIndexPath:[tView indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES]; } 

Eso es. Sin cálculos en absoluto.

Estoy haciendo algo muy similar, es genérico, no hay necesidad de calcular algo específico para tu código. Simplemente revisa las observaciones en el código:

En MyUIViewController.h

 @interface MyUIViewController: UIViewController <UITableViewDelegate, UITableViewDataSource> { UITableView *myTableView; UITextField *actifText; } @property (nonatomic, retain) IBOutlet UITableView *myTableView; @property (nonatomic, retain) IBOutlet UITextField *actifText; - (IBAction)textFieldDidBeginEditing:(UITextField *)textField; - (IBAction)textFieldDidEndEditing:(UITextField *)textField; -(void) keyboardWillHide:(NSNotification *)note; -(void) keyboardWillShow:(NSNotification *)note; @end 

En MyUIViewController.m

 @implementation MyUIViewController @synthesize myTableView; @synthesize actifText; - (void)viewDidLoad { // Register notification when the keyboard will be show [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; // Register notification when the keyboard will be hide [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; } // To be link with your TextField event "Editing Did Begin" // memoryze the current TextField - (IBAction)textFieldDidBeginEditing:(UITextField *)textField { self.actifText = textField; } // To be link with your TextField event "Editing Did End" // release current TextField - (IBAction)textFieldDidEndEditing:(UITextField *)textField { self.actifText = nil; } -(void) keyboardWillShow:(NSNotification *)note { // Get the keyboard size CGRect keyboardBounds; [[note.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue: &keyboardBounds]; // Detect orientation UIInterfaceOrientation orientation = [[UIApplication shanetworkingApplication] statusBarOrientation]; CGRect frame = self.myTableView.frame; // Start animation [UIView beginAnimations:nil context:NULL]; [UIView setAnimationBeginsFromCurrentState:YES]; [UIView setAnimationDuration:0.3f]; // Reduce size of the Table view if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) frame.size.height -= keyboardBounds.size.height; else frame.size.height -= keyboardBounds.size.width; // Apply new size of table view self.myTableView.frame = frame; // Scroll the table view to see the TextField just above the keyboard if (self.actifText) { CGRect textFieldRect = [self.myTableView convertRect:self.actifText.bounds fromView:self.actifText]; [self.myTableView scrollRectToVisible:textFieldRect animated:NO]; } [UIView commitAnimations]; } -(void) keyboardWillHide:(NSNotification *)note { // Get the keyboard size CGRect keyboardBounds; [[note.userInfo valueForKey:UIKeyboardFrameBeginUserInfoKey] getValue: &keyboardBounds]; // Detect orientation UIInterfaceOrientation orientation = [[UIApplication shanetworkingApplication] statusBarOrientation]; CGRect frame = self.myTableView.frame; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationBeginsFromCurrentState:YES]; [UIView setAnimationDuration:0.3f]; // Increase size of the Table view if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) frame.size.height += keyboardBounds.size.height; else frame.size.height += keyboardBounds.size.width; // Apply new size of table view self.myTableView.frame = frame; [UIView commitAnimations]; } @end 

Versión Swift 1.2+:

 class ViewController: UIViewController, UITextFieldDelegate { @IBOutlet weak var activeText: UITextField! @IBOutlet weak var tableView: UITableView! override func viewDidLoad() { NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil) } func textFieldDidBeginEditing(textField: UITextField) { activeText = textField } func textFieldDidEndEditing(textField: UITextField) { activeText = nil } func keyboardWillShow(note: NSNotification) { if let keyboardSize = (note.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() { var frame = tableView.frame UIView.beginAnimations(nil, context: nil) UIView.setAnimationBeginsFromCurrentState(true) UIView.setAnimationDuration(0.3) frame.size.height -= keyboardSize.height tableView.frame = frame if activeText != nil { let rect = tableView.convertRect(activeText.bounds, fromView: activeText) tableView.scrollRectToVisible(rect, animated: false) } UIView.commitAnimations() } } func keyboardWillHide(note: NSNotification) { if let keyboardSize = (note.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() { var frame = tableView.frame UIView.beginAnimations(nil, context: nil) UIView.setAnimationBeginsFromCurrentState(true) UIView.setAnimationDuration(0.3) frame.size.height += keyboardSize.height tableView.frame = frame UIView.commitAnimations() } } } 

Tuve el mismo problema pero noté que aparece solo en una vista. Entonces empecé a search las diferencias en los controlleres.

Descubrí que el comportamiento de desplazamiento está configurado en - (void)viewWillAppear:(BOOL)animated de la super instancia.

Así que asegúrate de implementar así:

 - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; // your code } 

Y no importa si usa UIViewController o UITableViewController ; lo comprobé poniendo un UITableView como una subvista de self.view en el UIViewController . Fue el mismo comportamiento. La vista no permitió desplazarse si la llamada [super viewWillAppear:animated]; Estaba faltando.

Creo que he llegado a la solución para que coincida con el comportamiento de las aplicaciones de Apple.

Primero, en su vista, aparecerá: suscribirse a las notifications del keyboard, para saber cuándo se mostrará y se ocultará el keyboard, y el sistema le dirá el tamaño del keyboard, pero no olvide anular el logging en su vista.

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

Implemente los methods similares a los siguientes para que ajuste el tamaño de su tablaView para que coincida con el área visible una vez que se muestre el keyboard. Aquí estoy rastreando el estado del keyboard por separado, así que puedo elegir cuándo establecer la tabla de nuevo a la altura completa yo mismo, ya que obtienes estas notifications en cada cambio de campo. No olvide implementar keyboardWillHide: y elija un lugar apropiado para corregir el tamaño de tableView.

 -(void) keyboardWillShow:(NSNotification *)note { CGRect keyboardBounds; [[note.userInfo valueForKey:UIKeyboardBoundsUserInfoKey] getValue: &keyboardBounds]; keyboardHeight = keyboardBounds.size.height; if (keyboardIsShowing == NO) { keyboardIsShowing = YES; CGRect frame = self.view.frame; frame.size.height -= keyboardHeight; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationBeginsFromCurrentState:YES]; [UIView setAnimationDuration:0.3f]; self.view.frame = frame; [UIView commitAnimations]; } } 

Ahora aquí está el desplazamiento, primero trabajamos algunos tamaños, luego vemos dónde estamos en el área visible y establecemos el rectángulo al que queremos desplazarnos para que sea la media vista arriba o debajo del centro del campo de text basado en donde está en la vista. En este caso, tenemos una matriz de UITextFields y una enumeración que los rastrea, por lo que multiplicar el valor de la fila por el número de fila nos da el desplazamiento real del marco dentro de esta vista externa.

 - (void) textFieldDidBeginEditing:(UITextField *)textField { CGRect frame = textField.frame; CGFloat rowHeight = self.tableView.rowHeight; if (textField == textFields[CELL_FIELD_ONE]) { frame.origin.y += rowHeight * CELL_FIELD_ONE; } else if (textField == textFields[CELL_FIELD_TWO]) { frame.origin.y += rowHeight * CELL_FIELD_TWO; } else if (textField == textFields[CELL_FIELD_THREE]) { frame.origin.y += rowHeight * CELL_FIELD_THREE; } else if (textField == textFields[CELL_FIELD_FOUR]) { frame.origin.y += rowHeight * CELL_FIELD_FOUR; } CGFloat viewHeight = self.tableView.frame.size.height; CGFloat halfHeight = viewHeight / 2; CGFloat midpoint = frame.origin.y + (textField.frame.size.height / 2); if (midpoint < halfHeight) { frame.origin.y = 0; frame.size.height = midpoint; } else { frame.origin.y = midpoint; frame.size.height = midpoint; } [self.tableView scrollRectToVisible:frame animated:YES]; } 

Esto parece funcionar bastante bien.

Es posible que haya extrañado esto, ya que no leí todo el artículo aquí, pero lo que encontré parece engañosamente simple. No puse esto en el escurridor, probando en todas las situaciones, pero parece que debería funcionar bien.

simplemente ajuste el contenido de la vista de tabla por la altura del keyboard y, a continuación, desplácese por la celda hacia abajo:

 - (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); self.myTableView.contentInset = contentInsets; self.myTableView.scrollIndicatorInsets = contentInsets; [self.myTableView scrollToRowAtIndexPath:self.currentField.indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES]; } 

y por supuesto

 - (void)keyboardWasHidden:(NSNotification *)aNotification { [UIView animateWithDuration:.3 animations:^(void) { self.myTableView.contentInset = UIEdgeInsetsZero; self.myTableView.scrollIndicatorInsets = UIEdgeInsetsZero; }]; } 

¿Es esto demasiado simple? ¿Me estoy perdiendo de algo? Hasta ahora funciona bien para mí, pero como dije, no lo puse a través del escurridor …

Si puede usar UITableViewController , obtendrá la funcionalidad de manera gratuita. A veces, sin embargo, esta no es una opción, específicamente si necesita varias vistas, no solo UITableView .

Algunas de las soluciones presentadas aquí no funcionan en iOS ≥4, algunas no funcionan en iPad o en modo horizontal, otras no funcionan para los keyboards Bluetooth (donde no queremos que se desplace), otras no funcionan al cambiar entre varios campos de text. Entonces, si elige alguna solución, asegúrese de probar estos casos. Esta es la solución que usamos en InAppSettingsKit :

 - (void)_keyboardWillShow:(NSNotification*)notification { if (self.navigationController.topViewController == self) { NSDictionary* userInfo = [notification userInfo]; // we don't use SDK constants here to be universally compatible with all SDKs ≥ 3.0 NSValue* keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardBoundsUserInfoKey"]; if (!keyboardFrameValue) { keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardFrameEndUserInfoKey"]; } // Reduce the tableView height by the part of the keyboard that actually covers the tableView CGRect windowRect = [[UIApplication shanetworkingApplication] keyWindow].bounds; if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) { windowRect = IASKCGRectSwap(windowRect); } CGRect viewRectAbsolute = [_tableView convertRect:_tableView.bounds toView:[[UIApplication shanetworkingApplication] keyWindow]]; if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) { viewRectAbsolute = IASKCGRectSwap(viewRectAbsolute); } CGRect frame = _tableView.frame; frame.size.height -= [keyboardFrameValue CGRectValue].size.height - CGRectGetMaxY(windowRect) + CGRectGetMaxY(viewRectAbsolute); [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]]; [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]]; _tableView.frame = frame; [UIView commitAnimations]; UITableViewCell *textFieldCell = (id)((UITextField *)self.currentFirstResponder).superview.superview; NSIndexPath *textFieldIndexPath = [_tableView indexPathForCell:textFieldCell]; // iOS 3 sends hide and show notifications right after each other // when switching between textFields, so cancel -scrollToOldPosition requests [NSObject cancelPreviousPerformRequestsWithTarget:self]; [_tableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES]; } } - (void) scrollToOldPosition { [_tableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES]; } - (void)_keyboardWillHide:(NSNotification*)notification { if (self.navigationController.topViewController == self) { NSDictionary* userInfo = [notification userInfo]; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]]; [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]]; _tableView.frame = self.view.bounds; [UIView commitAnimations]; [self performSelector:@selector(scrollToOldPosition) withObject:nil afterDelay:0.1]; } } 

Aquí está el código completo de la class en InAppSettingsKit. Para probarlo, use el panel secundario "Lista completa" donde puede probar los escenarios mencionados anteriormente.

La solución más simple para Swift 3 , basada en la solución Bartłomiej Semańczyk :

 override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver(self, selector: #selector(CreateEditRitualViewController.keyboardWillShow(notification:)), name: NSNotification.Name.UIKeyboardDidShow, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(CreateEditRitualViewController.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardDidHide, object: nil) } deinit { NotificationCenter.default.removeObserver(self) } // MARK: Keyboard Notifications func keyboardWillShow(notification: NSNotification) { if let keyboardHeight = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height { tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0) } } func keyboardWillHide(notification: NSNotification) { UIView.animate(withDuration: 0.2, animations: { // For some reason adding inset in keyboardWillShow is animated by itself but removing is not, that's why we have to use animateWithDuration here self.tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0) }) } 

La solución más simple para Swift :

 override func viewDidLoad() { super.viewDidLoad() searchBar?.becomeFirstResponder() NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MyViewController.keyboardWillShow(_:)), name: UIKeyboardDidShowNotification, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MyViewController.keyboardWillHide(_:)), name: UIKeyboardDidHideNotification, object: nil) } deinit { NSNotificationCenter.defaultCenter().removeObserver(self) } func keyboardWillShow(notification: NSNotification) { if let userInfo = notification.userInfo { if let keyboardHeight = userInfo[UIKeyboardFrameEndUserInfoKey]?.CGRectValue.size.height { tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0) } } } func keyboardWillHide(notification: NSNotification) { UIView.animateWithDuration(0.2, animations: { self.table_create_issue.contentInset = UIEdgeInsetsMake(0, 0, 0, 0) }) // For some reason adding inset in keyboardWillShow is animated by itself but removing is not, that's why we have to use animateWithDuration here } 

Combinando y completando los espacios en blanco a partir de varias respuestas (en particular Ortwin Gentz, usuario 98013) y otra publicación, esto funcionará de forma inmediata para el SDK 4.3 en un iPad en modo Retrato u Paisaje:

 @implementation UIView (FindFirstResponder) - (UIResponder *)findFirstResponder { if (self.isFirstResponder) { return self; } for (UIView *subView in self.subviews) { UIResponder *firstResponder = [subView findFirstResponder]; if (firstResponder != nil) { return firstResponder; } } return nil; } @end @implementation MyViewController - (UIResponder *)currentFirstResponder { return [self.view findFirstResponder]; } - (IBAction)editingEnded:sender { [sender resignFirstResponder]; } - (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; return NO; } - (void)textFieldDidBeginEditing:(UITextField *)textField { UITableViewCell *cell = (UITableViewCell*) [[textField superview] superview]; [_tableView scrollToRowAtIndexPath:[_tableView indexPathForCell:cell] atScrollPosition:UITableViewScrollPositionTop animated:YES]; } - (void)keyboardWillShow:(NSNotification*)notification { if ([self currentFirstResponder] != nil) { NSDictionary* userInfo = [notification userInfo]; // we don't use SDK constants here to be universally compatible with all SDKs ≥ 3.0 NSValue* keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardBoundsUserInfoKey"]; if (!keyboardFrameValue) { keyboardFrameValue = [userInfo objectForKey:@"UIKeyboardFrameEndUserInfoKey"]; } // Reduce the tableView height by the part of the keyboard that actually covers the tableView CGRect windowRect = [[UIApplication shanetworkingApplication] keyWindow].bounds; CGRect viewRectAbsolute = [_tableView convertRect:_tableView.bounds toView:[[UIApplication shanetworkingApplication] keyWindow]]; CGRect frame = _tableView.frame; if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) { windowRect = CGRectMake(windowRect.origin.y, windowRect.origin.x, windowRect.size.height, windowRect.size.width); viewRectAbsolute = CGRectMake(viewRectAbsolute.origin.y, viewRectAbsolute.origin.x, viewRectAbsolute.size.height, viewRectAbsolute.size.width); } frame.size.height -= [keyboardFrameValue CGRectValue].size.height - CGRectGetMaxY(windowRect) + CGRectGetMaxY(viewRectAbsolute); [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]]; [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]]; _tableView.frame = frame; [UIView commitAnimations]; UITableViewCell *textFieldCell = (id)((UITextField *)self.currentFirstResponder).superview.superview; NSIndexPath *textFieldIndexPath = [_tableView indexPathForCell:textFieldCell]; // iOS 3 sends hide and show notifications right after each other // when switching between textFields, so cancel -scrollToOldPosition requests [NSObject cancelPreviousPerformRequestsWithTarget:self]; _topmostRowBeforeKeyboardWasShown = [[_tableView indexPathsForVisibleRows] objectAtIndex:0]; [_tableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES]; } } - (void) scrollToOldPosition { [_tableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES]; } - (void)keyboardWillHide:(NSNotification*)notification { if ([self currentFirstResponder] != nil) { NSDictionary* userInfo = [notification userInfo]; [UIView beginAnimations:nil context:NULL]; [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]]; [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]]; _tableView.frame = self.view.bounds; [UIView commitAnimations]; [self performSelector:@selector(scrollToOldPosition) withObject:nil afterDelay:0.1]; } } @end 

Espero que ya tengan una solución para leer todos esos. Pero encontré mi solución de la siguiente manera. Espero que ya tenga una celda con UITextField. Entonces, al prepararse, simplemente mantenga el índice de fila en la label del campo de text.

 cell.textField.tag = IndexPath.row; 

Cree un activeTextField, instancia de UITextField con scope global como a continuación:

 @interface EditViewController (){ UITextField *activeTextField; } 

Entonces, ahora solo copys pegar mi código al final. Y también no te olvides de agregar UITextFieldDelegate

 #pragma mark - TextField Delegation - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{ activeTextField = textField; return YES; } - (void)textFieldDidEndEditing:(UITextField *)textField{ activeTextField = nil; } 

Registra notifications de keyboard

 #pragma mark - Keyboard Activity - (void)registerForKeyboardNotifications { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillBeHidden:) name:UIKeyboardWillHideNotification object:nil]; } 

Maneja notifications de keyboard:

Se llama cuando se envía UIKeyboardDidShowNotification.

 - (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); [self.tableView setContentInset:contentInsets]; [self.tableView setScrollIndicatorInsets:contentInsets]; NSIndexPath *currentRowIndex = [NSIndexPath indexPathForRow:activeTextField.tag inSection:0]; [self.tableView scrollToRowAtIndexPath:currentRowIndex atScrollPosition:UITableViewScrollPositionTop animated:YES]; } 

Se llama cuando se envía UIKeyboardWillHideNotification

 - (void)keyboardWillBeHidden:(NSNotification*)aNotification { UIEdgeInsets contentInsets = UIEdgeInsetsZero; [self.tableView setContentInset:contentInsets]; [self.tableView setScrollIndicatorInsets:contentInsets]; } 

Ahora queda una cosa: invoque el método registerForKeyboardNotifications en el método ViewDidLoad de la siguiente manera:

 - (void)viewDidLoad { [super viewDidLoad]; // Registering keyboard notification [self registerForKeyboardNotifications]; // Your codes here... } 

Has terminado, espero que tus textFields ya no estén ocultos por el keyboard.

THE RIGHT ANSWER is Sam Ho's answer:

"If you use UITableViewController instead of UIViewController, it will automatically do so.".

Just make sure to connect your UITableView to the TableView property of the UITableViewController (so eg do not add it as a subview of the View property of the UITableViewController).

Also make sure to set the AutoresizingMask property of your UITableView to FlexibleHeight

Mi acercamiento:

I first subclass UITextField and add an indexPath property. In the cellFor… Method i hand over the indexPath property.

Then I add following code:

 UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:textField.indexPath]; CGPoint cellPoint = [cell convertPoint:textField.center toView:self.tableView]; [UIView animateWithDuration:0.3 animations:^(void){self.tableView.contentOffset = CGPointMake(0, cellPoint.y-50);}]; 

to the textFieldShould/WillBegin…etc.

When the Keyboard disappears you have to reverse it with:

 [UIView animateWithDuration:0.3 animations:^(void){self.tableView.contentOffset = CGPointMake(0, 0);}]; 

Keyboard notifications work, but Apple's sample code for that assumes that the scroll view is the root view of the window. This is usually not the case. You have to compensate for tab bars, etc., to get the right offset.

It is easier than it sounds. Here is the code I use in a UITableViewController. It has two instance variables, hiddenRect and keyboardShown.

 // Called when the UIKeyboardDidShowNotification is sent. - (void)keyboardWasShown:(NSNotification*)aNotification { if (keyboardShown) return; NSDictionary* info = [aNotification userInfo]; // Get the frame of the keyboard. NSValue *centerValue = [info objectForKey:UIKeyboardCenterEndUserInfoKey]; NSValue *boundsValue = [info objectForKey:UIKeyboardBoundsUserInfoKey]; CGPoint keyboardCenter = [centerValue CGPointValue]; CGRect keyboardBounds = [boundsValue CGRectValue]; CGPoint keyboardOrigin = CGPointMake(keyboardCenter.x - keyboardBounds.size.width / 2.0, keyboardCenter.y - keyboardBounds.size.height / 2.0); CGRect keyboardScreenFrame = { keyboardOrigin, keyboardBounds.size }; // Resize the scroll view. UIScrollView *scrollView = (UIScrollView *) self.tableView; CGRect viewFrame = scrollView.frame; CGRect keyboardFrame = [scrollView.superview convertRect:keyboardScreenFrame fromView:nil]; hiddenRect = CGRectIntersection(viewFrame, keyboardFrame); CGRect remainder, slice; CGRectDivide(viewFrame, &slice, &remainder, CGRectGetHeight(hiddenRect), CGRectMaxYEdge); scrollView.frame = remainder; // Scroll the active text field into view. CGRect textFieldRect = [/* selected cell */ frame]; [scrollView scrollRectToVisible:textFieldRect animated:YES]; keyboardShown = YES; } // Called when the UIKeyboardDidHideNotification is sent - (void)keyboardWasHidden:(NSNotification*)aNotification { // Reset the height of the scroll view to its original value UIScrollView *scrollView = (UIScrollView *) self.tableView; CGRect viewFrame = [scrollView frame]; scrollView.frame = CGRectUnion(viewFrame, hiddenRect); keyboardShown = NO; } 

If you use Three20 , then use the autoresizesForKeyboard property. Just set in the your view controller's -initWithNibName:bundle method

 self.autoresizesForKeyboard = YES 

This takes care of:

  1. Listening for keyboard notifications and adjusting the table view's frame
  2. Scrolling to the first responder

Hecho y hecho.

Use UITextField's delegate method :

Rápido

 func textFieldShouldBeginEditing(textField: UITextField) -> bool { let txtFieldPosition = textField.convertPoint(textField.bounds.origin, toView: yourTableViewHere) let indexPath = yourTablViewHere.indexPathForRowAtPoint(txtFieldPosition) if indexPath != nil { yourTablViewHere.scrollToRowAtIndexPath(indexPath!, atScrollPosition: .Top, animated: true) } return true } 

C objective

 - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { CGPoint txtFieldPosition = [textField convertPoint:CGPointZero toView: yourTablViewHere]; NSLog(@"Begin txtFieldPosition : %@",NSStringFromCGPoint(txtFieldPosition)); NSIndexPath *indexPath = [yourTablViewHere indexPathForRowAtPoint:txtFieldPosition]; if (indexPath != nil) { [yourTablViewHere scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES]; } return YES; } 

If you use a uitableview to place your textfields ( from Jeff Lamarche ), you can just scroll the tableview using the delegate method like so.

(Note: my text fields are stonetworking in an array with the same index as there row in the tableview)

 - (void) textFieldDidBeginEditing:(UITextField *)textField { int index; for(UITextField *aField in textFields){ if (textField == aField){ index = [textFields indexOfObject:aField]-1; } } if(index >= 0) [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:YES]; [super textFieldDidBeginEditing:textField]; } 

A more stream-lined solution. It slips into the UITextField delegate methods, so it doesn't require messing w/ UIKeyboard notifications.

Implementation notes:

kSettingsRowHeight — the height of a UITableViewCell.

offsetTarget and offsetThreshold are baed off of kSettingsRowHeight. If you use a different row height, set those values to point's y property. [alt: calculate the row offset in a different manner.]

 - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { CGFloat offsetTarget = 113.0f; // 3rd row CGFloat offsetThreshold = 248.0f; // 6th row (ie 2nd-to-last row) CGPoint point = [self.tableView convertPoint:CGPointZero fromView:textField]; [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:0.2]; [UIView setAnimationCurve:UIViewAnimationCurveEaseOut]; CGRect frame = self.tableView.frame; if (point.y > offsetThreshold) { self.tableView.frame = CGRectMake(0.0f, offsetTarget - point.y + kSettingsRowHeight, frame.size.width, frame.size.height); } else if (point.y > offsetTarget) { self.tableView.frame = CGRectMake(0.0f, offsetTarget - point.y, frame.size.width, frame.size.height); } else { self.tableView.frame = CGRectMake(0.0f, 0.0f, frame.size.width, frame.size.height); } [UIView commitAnimations]; return YES; 

}

 - (BOOL)textFieldShouldReturn:(UITextField *)textField { [textField resignFirstResponder]; [UIView beginAnimations:nil context:nil]; [UIView setAnimationBeginsFromCurrentState:YES]; [UIView setAnimationDuration:0.2]; [UIView setAnimationCurve:UIViewAnimationCurveEaseOut]; CGRect frame = self.tableView.frame; self.tableView.frame = CGRectMake(0.0f, 0.0f, frame.size.width, frame.size.height); [UIView commitAnimations]; return YES; 

}

I ran into something like your problem (I wanted a screen similar to the iPhone's settings.app with a bunch of editable cells stacked on on top of another) and found that this approach worked well:

sliding uitextfields around to avoid

Since you have textfields in a table, the best way really is to resize the table – you need to set the tableView.frame to be smaller in height by the size of the keyboard (I think around 165 pixels) and then expand it again when the keyboard is dismissed.

You can optionally also disable user interaction for the tableView at that time as well, if you do not want the user scrolling.

This soluton works for me, PLEASE note the line

 [tableView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height+160) animated:YES]; 

You can change the 160 value to match it work with you

 - (void)keyboardWasShown:(NSNotification*)aNotification { NSDictionary* info = [aNotification userInfo]; CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; CGRect bkgndRect = activeField.superview.frame; bkgndRect.size.height += kbSize.height; [activeField.superview setFrame:bkgndRect]; [tableView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height+160) animated:YES]; } - (void)textFieldDidBeginEditing:(UITextField *)textField { activeField = textField; } -(void)textFieldDidEndEditing:(UITextField *)textField { activeField = nil; } // Called when the UIKeyboardWillHideNotification is sent - (void)keyboardWillBeHidden:(NSNotification*)aNotification { UIEdgeInsets contentInsets = UIEdgeInsetsZero; tableView.contentInset = contentInsets; tableView.scrollIndicatorInsets = contentInsets; NSDictionary* info = [aNotification userInfo]; CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; CGRect bkgndRect = activeField.superview.frame; //bkgndRect.size.height += kbSize.height; [activeField.superview setFrame:bkgndRect]; [tableView setContentOffset:CGPointMake(0.0, activeField.frame.origin.y-kbSize.height) animated:YES]; } 

Very interesting discussion thread, i also faced the same problem may be worse one because

  1. I was using a custom cell and the textfield was inside that.
  2. I had to use UIViewController to meet my requirements so cant take advantage of UITableViewController.
  3. I had filter/ sort criterias in my table cell, ie ur cells keeps on changing and keeping track of the indexpath and all will not help.

So read the threads here and implemented my version, which helped me in pushing up my contents in iPad in landscape mode. Here is code ( this is not fool proof and all, but it fixed my issue) First u need to have a delegate in your custom cell class, which on editing begins, sends the textfield to ur viewcontroller and set the activefield = theTextField there

// IMPLEMENTED TO HANDLE LANDSCAPE MODE ONLY

 - (void)keyboardWasShown:(NSNotification*)aNotification { NSDictionary* info = [aNotification userInfo]; CGSize kbValue = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; CGRect aRect = myTable.frame; CGSize kbSize = CGSizeMake(kbValue.height, kbValue.width); aRect.size.height -= kbSize.height+50; // This will the exact rect in which your textfield is present CGRect rect = [myTable convertRect:activeField.bounds fromView:activeField]; // Scroll up only if requinetworking if (!CGRectContainsPoint(aRect, rect.origin) ) { [myTable setContentOffset:CGPointMake(0.0, rect.origin.y) animated:YES]; } } 

// Called when the UIKeyboardWillHideNotification is sent

 - (void)keyboardWillHide:(NSNotification*)aNotification { UIEdgeInsets contentInsets = UIEdgeInsetsZero; myTable.contentInset = contentInsets; myTable.scrollIndicatorInsets = contentInsets; NSDictionary* info = [aNotification userInfo]; CGSize kbValue = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; CGSize kbSize = CGSizeMake(kbValue.height, kbValue.width); CGRect bkgndRect = activeField.superview.frame; bkgndRect.size.height += kbSize.height; [activeField.superview setFrame:bkgndRect]; [myTable setContentOffset:CGPointMake(0.0, 10.0) animated:YES]; } 

-anoop4real

An example in Swift, using the exact point of the text field from Get indexPath of UITextField in UITableViewCell with Swift :

 func textFieldDidBeginEditing(textField: UITextField) { let pointInTable = textField.convertPoint(textField.bounds.origin, toView: self.accountsTableView) let textFieldIndexPath = self.accountsTableView.indexPathForRowAtPoint(pointInTable) accountsTableView.scrollToRowAtIndexPath(textFieldIndexPath!, atScrollPosition: .Top, animated: true) } 

This works perfectly, and on iPad too.

 - (BOOL)textFieldShouldReturn:(UITextField *)textField { if(textField == textfield1){ [accountName1TextField becomeFirstResponder]; }else if(textField == textfield2){ [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES]; [textfield3 becomeFirstResponder]; }else if(textField == textfield3){ [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES]; [textfield4 becomeFirstResponder]; }else if(textField == textfield4){ [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES]; [textfield5 becomeFirstResponder]; }else if(textField == textfield5){ [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:3 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES]; [textfield6 becomeFirstResponder]; }else if(textField == textfield6){ [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:4 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES]; [textfield7 becomeFirstResponder]; }else if(textField == textfield7){ [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:5 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES]; [textfield8 becomeFirstResponder]; }else if(textField == textfield8){ [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:6 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES]; [textfield9 becomeFirstResponder]; }else if(textField == textfield9){ [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:7 inSection:1] atScrollPosition:UITableViewScrollPositionTop animated:YES]; [textField resignFirstResponder]; } 

So after hours of grueling work trying to use these current solutions (and utterly failing) I finally got things working well, and updated them to use the new animation blocks. My answer is entirely based on Ortwin's answer above .

So for whatever reason the code above was just not working for me. My setup seemed fairly similar to others, but maybe because I was on an iPad or 4.3… no idea. It was doing some wacky math and shooting my tableview off the screen.

See end result of my solution: http://screencast.com/t/hjBCuRrPC (Please ignore the photo. :-P)

So I went with the gist of what Ortwin was doing, but changed how it was doing some math to add up the origin.y & size.height of my table view with the height of the keyboard. When I subtract the height of the window from that result , it tells me how much intersection I have going on. If its greater than 0 (aka there is some overlap) I perform the animation of the frame height.

In addition there were some networkingraw issues that were solved by 1) Waiting to scroll to the cell until the animation was done and 2) using the UIViewAnimationOptionBeginFromCurrentState option when hiding the keyboard.

A couple things to note.

  • _topmostRowBeforeKeyboardWasShown & _originalFrame are instance variables declanetworking in the header.
  • self.guestEntryTableView is my tableView (I'm in an external file)
  • IASKCGRectSwap is Ortwin's method for flipping the coordinates of a frame
  • I only update the height of the tableView if at least 50px of it is going to be showing
  • Since I'm not in a UIViewController I don't have self.view, so I just return the tableView to its original frame

Again, I wouldn't have gotten near this answer if I Ortwin didn't provide the crux of it. Here's the code:

 - (IBAction)textFieldDidBeginEditing:(UITextField *)textField { self.activeTextField = textField; if ([self.guestEntryTableView indexPathsForVisibleRows].count) { _topmostRowBeforeKeyboardWasShown = (NSIndexPath*)[[self.guestEntryTableView indexPathsForVisibleRows] objectAtIndex:0]; } else { // this should never happen _topmostRowBeforeKeyboardWasShown = [NSIndexPath indexPathForRow:0 inSection:0]; [textField resignFirstResponder]; } } - (IBAction)textFieldDidEndEditing:(UITextField *)textField { self.activeTextField = nil; } - (void)keyboardWillShow:(NSNotification*)notification { NSDictionary* userInfo = [notification userInfo]; NSValue* keyboardFrameValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey]; // Reduce the tableView height by the part of the keyboard that actually covers the tableView UIInterfaceOrientation orientation = [[UIApplication shanetworkingApplication] statusBarOrientation]; CGRect windowRect = [[UIApplication shanetworkingApplication] keyWindow].bounds; CGRect viewRectAbsolute = [self.guestEntryTableView convertRect:self.guestEntryTableView.bounds toView:[[UIApplication shanetworkingApplication] keyWindow]]; CGRect keyboardFrame = [keyboardFrameValue CGRectValue]; if (UIInterfaceOrientationLandscapeLeft == orientation ||UIInterfaceOrientationLandscapeRight == orientation ) { windowRect = IASKCGRectSwap(windowRect); viewRectAbsolute = IASKCGRectSwap(viewRectAbsolute); keyboardFrame = IASKCGRectSwap(keyboardFrame); } // fix the coordinates of our rect to have a top left origin 0,0 viewRectAbsolute = FixOriginRotation(viewRectAbsolute, orientation, windowRect.size.width, windowRect.size.height); CGRect frame = self.guestEntryTableView.frame; _originalFrame = self.guestEntryTableView.frame; int remainder = (viewRectAbsolute.origin.y + viewRectAbsolute.size.height + keyboardFrame.size.height) - windowRect.size.height; if (remainder > 0 && !(remainder > frame.size.height + 50)) { frame.size.height = frame.size.height - remainder; float duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; [UIView animateWithDuration: duration animations:^{ self.guestEntryTableView.frame = frame; } completion:^(BOOL finished){ UITableViewCell *textFieldCell = (UITableViewCell*) [[self.activeTextField superview] superview]; NSIndexPath *textFieldIndexPath = [self.guestEntryTableView indexPathForCell:textFieldCell]; [self.guestEntryTableView scrollToRowAtIndexPath:textFieldIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES]; }]; } } - (void)keyboardWillHide:(NSNotification*)notification { NSDictionary* userInfo = [notification userInfo]; float duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]; [UIView animateWithDuration: duration delay: 0.0 options: (UIViewAnimationOptionBeginFromCurrentState) animations:^{ self.guestEntryTableView.frame = _originalFrame; } completion:^(BOOL finished){ [self.guestEntryTableView scrollToRowAtIndexPath:_topmostRowBeforeKeyboardWasShown atScrollPosition:UITableViewScrollPositionTop animated:YES]; }]; } #pragma mark CGRect Utility function CGRect IASKCGRectSwap(CGRect rect) { CGRect newRect; newRect.origin.x = rect.origin.y; newRect.origin.y = rect.origin.x; newRect.size.width = rect.size.height; newRect.size.height = rect.size.width; return newRect; } CGRect FixOriginRotation(CGRect rect, UIInterfaceOrientation orientation, int parentWidth, int parentHeight) { CGRect newRect; switch(orientation) { case UIInterfaceOrientationLandscapeLeft: newRect = CGRectMake(parentWidth - (rect.size.width + rect.origin.x), rect.origin.y, rect.size.width, rect.size.height); break; case UIInterfaceOrientationLandscapeRight: newRect = CGRectMake(rect.origin.x, parentHeight - (rect.size.height + rect.origin.y), rect.size.width, rect.size.height); break; case UIInterfaceOrientationPortrait: newRect = rect; break; case UIInterfaceOrientationPortraitUpsideDown: newRect = CGRectMake(parentWidth - (rect.size.width + rect.origin.x), parentHeight - (rect.size.height + rect.origin.y), rect.size.width, rect.size.height); break; } return newRect; } 

Another easy method (only works with one section)

 //cellForRowAtIndexPath UItextField *tf; [cell addSubview:tf]; tf.tag = indexPath.row; tf.delegate = self; //textFieldDidBeginEditing:(UITextField *)text [[self.tableView scrollToRowsAtIndexPath:[NSIndexPath indexPathForRow:text.tag in section:SECTIONINTEGER] animated:YES]; 

If your UITableView is managed by a subclass of UITableViewController and not UITableView, and the text field delegate is the UITableViewController, it should manage all the scrolling automatically — all these other comments are very difficult to implement in practice.

For a good example see the apple example code project: TaggedLocations.

You can see that it scrolls automatically, but there doesn't seem to be any code that does this. This project also has custom table view cells, so if you build your application with it as a guide, you should get the desinetworking result.

Here is how I made this work, which is a mixture of Sam Ho and Marcel W's answers, and some of my own bug fixes made to my crappy code. I was using a UITableViewController. The table now resizes correctly when the keyboard is shown.

1) In viewDidLoad I added:

self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

2) I had forgotten to call the super equivalents in viewWillAppear and awakeFromNib . I added these back in.

I have just solved such a problem by myself after I refernetworking a mass of solutions found via Google and Stack Overflow.

First, please assure that you have set up an IBOutlet of your UIScrollView, Then please take a close look at Apple Doc: Keyboard Management . Finally, if you can scroll the background, but the keyboard still covers the Text Fields, please have a look at this piece of code:

 // 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 (aRect.size.height < activeField.frame.origin.y+activeField.frame.size.height) { CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y+activeField.frame.size.height-aRect.size.height); [scrollView setContentOffset:scrollPoint animated:YES]; 

The main difference between this piece and Apple's lies in the if condition. I believe apple's calculation of scroll distance and condition of whether text field covenetworking by keyboard are not accurate, so I made my modification as above.

Let me know if it works

Easy and fast solution.

I just scroll to the right cell whenever scrolling happens

  - (void)scrollViewDidScroll:(UIScrollView *)scrollView 

Assuming I know table now is in this mode "_keepMyCellOnTop" & I know selected cell "_selectedCellIndex" or scroll to selected cell

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (_keepMyCellOnTop) { [self.tableView scrollToRowAtIndexPath:_selectedCellIndex atScrollPosition:UITableViewScrollPositionTop animated:NO]; } } 

This will prevent scrolling.

Placing the code in -(void) scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView will result a scroll up and down