Faire défiler un UITableView lorsque le champ de text est sélectionné

Après beaucoup d'essais et d'erreurs, j'abandonne et je pose la question. J'ai vu beaucoup de gens avec des problèmes similaires mais je n'arrive pas à get toutes les réponses pour bien fonctionner.

J'ai un UITableView qui est composé de cellules personnalisées. Les cellules sont constituées de 5 champs de text l'un à côté de l'autre (en quelque sorte comme une grid).

Lorsque j'essaie de faire défiler et d'éditer les cellules au bas de UITableView , je n'arrive pas à UITableView correctement mes cellules au-dessus du keyboard.

J'ai vu de nombreuses réponses parler de changer la taille des vues, etc … mais aucun d'entre eux a bien fonctionné jusqu'à présent.

Quelqu'un pourrait-il clarifier la «bonne» façon de le faire avec un exemple de code concret?

Si vous utilisez UITableViewController au lieu de UIViewController, il le fera automatiquement.

La fonction qui fait défiler pourrait être beaucoup plus 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]; } 

C'est tout. Pas de calculs du tout.

Je fais quelque chose de très similaire c'est générique, pas besoin de calculer quelque chose de spécifique pour votre code. Il suffit de vérifier les remarques sur le code:

Dans 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 

Dans 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 sharedApplication] 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 sharedApplication] 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 

Version 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() } } } 

J'ai eu le même problème mais j'ai remarqué qu'il n'apparaît que dans une vue. J'ai donc commencé à chercher les différences dans les controllers.

J'ai découvert que le comportement de défilement est défini dans - (void)viewWillAppear:(BOOL)animated de la super instance.

Alors assurez-vous de mettre en œuvre comme ceci:

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

Et ce n'est pas grave si vous utilisez UIViewController ou UITableViewController ; vérifié en mettant un UITableView comme une sous-vue de self.view dans le UIViewController . C'était le même comportement. La vue n'a pas permis de faire défiler si l'appel [super viewWillAppear:animated]; manquait.

Je pense que j'ai trouvé la solution pour correspondre au comportement des applications d'Apple.

Tout d'abord, dans votre vueWillAppear: abonnez-vous aux notifications du keyboard, de sorte que vous savez quand le keyboard va afficher et masquer, et le système vous dira la taille du keyboard, mais ne pas oublier de vous désinscrire dans votre vueWillDisappear :.

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

Implémentez les methods similaires à ci-dessous afin d'ajuster la taille de votre tableView pour qu'elle corresponde à la zone visible une fois le keyboard affiché. Ici, je suis suivi l'état du keyboard séparément afin que je puisse choisir quand remettre le tableau à sa pleine hauteur, puisque vous obtenez ces notifications sur chaque changement de champ. N'oubliez pas d'implémenter keyboardWillHide: et choisissez un location approprié pour corriger la taille de votre 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]; } } 

Maintenant, voici le bit de défilement, nous travaillons d'abord quelques tailles, puis nous voyons où nous sums dans la zone visible, et définissons le rect que nous voulons faire défiler pour être soit la demi-vue au-dessus ou au-dessous du milieu du champ de text là où il est dans la vue. Dans ce cas, nous avons un tableau de UITextFields et une énumération qui en garde la trace, donc multiplier rowHeight par le numéro de ligne nous donne le décalage réel de l'image dans cette vue externe.

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

Cela semble fonctionner très bien.

J'ai peut-être manqué cela, car je n'ai pas lu tout le message ici, mais ce que j'ai trouvé semble trompeusement simple. Je n'ai pas mis ça à l'épreuve, en testant dans toutes les situations, mais il me semble que ça devrait marcher.

il suffit d'ajuster le contentInset de la tableview par la hauteur du keyboard, puis faites défiler la cellule vers le bas:

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

et bien sûr

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

est-ce trop simple? Ai-je raté quelque chose? jusqu'à présent, ça fonctionne pour moi bien, mais comme je l'ai dit, je ne l'ai pas mis à travers l'essoreuse …

Si vous pouvez utiliser UITableViewController , vous obtenez la fonctionnalité gratuitement. Parfois, cependant, ce n'est pas une option, en particulier si vous avez besoin de plusieurs vues, pas seulement UITableView .

Certaines des solutions présentées ici ne fonctionnent pas sur iOS ≥4, certaines ne fonctionnent pas sur iPad ou en mode paysage, d'autres ne fonctionnent pas pour les keyboards Bluetooth (où nous ne voulons pas de défilement), d'autres ne le font pas travailler lors de la commutation entre plusieurs champs de text. Donc, si vous choisissez une solution, assurez-vous de tester ces cas. C'est la solution que nous utilisons dans 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 sharedApplication] keyWindow].bounds; if (UIInterfaceOrientationLandscapeLeft == self.interfaceOrientation ||UIInterfaceOrientationLandscapeRight == self.interfaceOrientation ) { windowRect = IASKCGRectSwap(windowRect); } CGRect viewRectAbsolute = [_tableView convertRect:_tableView.bounds toView:[[UIApplication sharedApplication] 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]; } } 

Voici le code complet de la class dans InAppSettingsKit. Pour le tester, utilisez le volet enfant "Liste complète" dans lequel vous pouvez tester les scénarios mentionnés ci-dessus.

La solution la plus simple pour Swift 3 , basée sur la solution 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 solution la plus simple pour 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 } 

En combinant et en complétant les blancs à partir de plusieurs réponses (notamment Ortwin Gentz, user 98013) et d'un autre post, cela fonctionnera hors de la boîte pour SDK 4.3 sur un iPad en mode Portrait ou Paysage:

 @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 sharedApplication] keyWindow].bounds; CGRect viewRectAbsolute = [_tableView convertRect:_tableView.bounds toView:[[UIApplication sharedApplication] 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 

J'espère que vous avez déjà trouvé une solution pour tout lire. Mais j'ai trouvé ma solution comme suit. Je m'attends à ce que vous ayez déjà une cellule avec UITextField. Donc, lors de la préparation, gardez simplement l'index des lignes dans l'label du champ de text.

 cell.textField.tag = IndexPath.row; 

Créez un activeTextField, instance de UITextField avec l'étendue globale comme ci-dessous:

 @interface EditViewController (){ UITextField *activeTextField; } 

Donc, maintenant vous copyz simplement mon code à la fin. Et n'oubliez pas d'append UITextFieldDelegate

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

Registres des 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]; } 

Handles Keyboard Notifications:

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

Called when the UIKeyboardWillHideNotification is sent

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

Now one thing is left, Call the registerForKeyboardNotifications method in to ViewDidLoad method as follows:

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

You are done, hope your textFields will no longer hidden by the 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

Mon approche:

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

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

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

Done and done.

Use UITextField's delegate method :

Rapide

 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 } 

Objectif c

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

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

I sortinged almost the same approach and came up with a simpler and smaller code for the same. I created a IBOutlet iTextView and associated with the UITextView in the IB.

  -(void)keyboardWillShow:(NSNotification *)notification { NSLog(@"Keyboard"); CGRect keyFrame = [[[notification userInfo]objectForKey:UIKeyboardFrameEndUserInfoKey]CGRectValue]; [UIView beginAnimations:@"resize view" context:nil]; [UIView setAnimationCurve:1]; [UIView setAnimationDuration:1.0]; CGRect frame = iTableView.frame; frame.size.height = frame.size.height - keyFrame.size.height; iTableView.frame = frame; [iTableView scrollRectToVisible:frame animated:YES]; [UIView commitAnimations]; } 

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 redraw 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 declared 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 sharedApplication] statusBarOrientation]; CGRect windowRect = [[UIApplication sharedApplication] keyWindow].bounds; CGRect viewRectAbsolute = [self.guestEntryTableView convertRect:self.guestEntryTableView.bounds toView:[[UIApplication sharedApplication] 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 desired 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 referred 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 covered by keyboard are not accurate, so I made my modification as above.

Let me know if it works