Après beaucoup d'essais et d'erreurs, j'abandonne et pose la question. J'ai vu beaucoup de gens avec des problèmes similaires mais je ne peux pas obtenir toutes les réponses pour bien travailler.
J'ai un UITableView
qui est composé de cellules personnalisées. Les cellules sont constituées de 5 champs de texte juxtaposés (un peu comme une grille).
Lorsque j'essaie de faire défiler et d'éditer les cellules au bas de la UITableView
, je ne parviens pas à positionner correctement mes cellules au-dessus du clavier.
J'ai vu de nombreuses réponses parler de changements de tailles de vues, etc., mais aucune d'entre elles n'a bien fonctionné jusqu'à présent.
Quelqu'un pourrait-il préciser la "bonne" façon de procéder avec un exemple de code concret?
Si vous utilisez UITableViewController au lieu de UIViewController, il le fera automatiquement.
La fonction qui fait le défilement 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. Aucun calcul 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 ..__, vérifiez 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
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'apparaissait que dans une vue. Alors j'ai commencé à chercher les différences entre les contrôleurs.
J'ai découvert que le comportement de défilement est défini dans - (void)viewWillAppear:(BOOL)animated
de la super instance.
Veillez donc à implémenter comme ceci:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// your code
}
Et peu importe si vous utilisez UIViewController
ou UITableViewController
; vérifié en mettant une UITableView
comme une sous-vue de self.view dans la UIViewController
. C'était le même comportement. La vue ne permettait pas de faire défiler si l'appel [super viewWillAppear:animated];
était manquant.
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
@objc func keyboardWillShow(notification: NSNotification) {
if let keyboardHeight = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height {
tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardHeight, 0)
}
}
@objc 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)
})
}
j'ai peut-être manqué cela, car je n'ai pas lu le post en entier, mais ce que j'ai proposé semble d'une simplicité trompeuse. Je n'ai pas mis cela à rude épreuve, à tester dans toutes les situations, mais il semble que cela devrait fonctionner très bien.
ajustez simplement le contentInset de la table par la hauteur du clavier, 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 sur
- (void)keyboardWasHidden:(NSNotification *)aNotification
{
[UIView animateWithDuration:.3 animations:^(void)
{
self.myTableView.contentInset = UIEdgeInsetsZero;
self.myTableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}];
}
est-ce trop simple? est-ce que je manque quelque chose? jusqu'à présent, cela fonctionne bien pour moi, mais comme je l'ai dit, je ne l'ai pas fait passer à travers l'essoreuse ...
Je pense avoir trouvé la solution qui correspond au comportement des applications Apple.
Tout d’abord, dans votre viewWillAppear: abonnez-vous aux notifications du clavier pour que vous sachiez quand le clavier sera affiché et masqué, et le système vous indiquera la taille du clavier, mais n’oubliez pas de vous désinscrire de votre viewWillDisappear :.
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
Implémentez des méthodes similaires à celles décrites ci-dessous pour ajuster la taille de votre tableView en fonction de la zone visible une fois que le clavier est affiché. Ici, je surveille l’état du clavier séparément afin de pouvoir choisir moi-même quand redéfinir la tableView à pleine hauteur, puisque vous recevez ces notifications à chaque changement de champ. N'oubliez pas d'implémenter keyboardWillHide: et choisissez un emplacement approprié pour corriger la taille de votre table.
-(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 sur quelques tailles, puis nous voyons où nous nous situons dans la zone visible et nous définissons le rect auquel nous voulons faire défiler la moitié de la vue au-dessus ou au-dessous du milieu du champ de texte. sur où il est dans la vue. Dans ce cas, nous avons un tableau de UITextFields et une énumération qui les garde, donc multiplier le paramètre rowHeight par le numéro de la ligne nous donne le décalage réel du cadre 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 assez bien.
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 de la 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 sur les claviers Bluetooth (pour lesquels nous ne voulons pas de défilement), d'autres pas fonctionne lors de la commutation entre plusieurs champs de texte. Donc, si vous choisissez une solution, assurez-vous de tester ces cas. C’est la solution que nous utilisation used 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 classe 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 :
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
}
J'espère que vous avez déjà une solution en lisant tous ceux-là. Mais j'ai trouvé ma solution comme suit. Je m'attends à ce que vous ayez déjà une cellule avec UITextField
. Donc, en cours de préparation, conservez simplement l'index de la ligne dans la balise du champ de texte.
cell.textField.tag = IndexPath.row;
Créez une instance activeTextField
de UITextField
avec une étendue globale comme ci-dessous:
@interface EditViewController (){
UITextField *activeTextField;
}
Donc, maintenant, il suffit de copier coller mon code à la fin. Et n'oubliez pas d'ajouter UITextFieldDelegate
#pragma mark - TextField Delegation
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
activeTextField = textField;
return YES;
}
- (void)textFieldDidEndEditing:(UITextField *)textField{
activeTextField = nil;
}
Enregistre le clavier notifications
#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];
}
Poignées Clavier Notifications
:
Appelé lorsque la UIKeyboardDidShowNotification
est envoyée.
- (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];
}
Appelé lorsque la UIKeyboardWillHideNotification
est envoyée
- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
UIEdgeInsets contentInsets = UIEdgeInsetsZero;
[self.tableView setContentInset:contentInsets];
[self.tableView setScrollIndicatorInsets:contentInsets];
}
Maintenant, une chose est laissée, appelez la méthode registerForKeyboardNotifications
dans à la méthode ViewDidLoad
comme suit:
- (void)viewDidLoad {
[super viewDidLoad];
// Registering keyboard notification
[self registerForKeyboardNotifications];
// Your codes here...
}
Vous avez terminé, espérons que votre textFields
ne sera plus masqué par le clavier.
En combinant et en complétant les blancs de plusieurs réponses (en particulier Ortwin Gentz, utilisateur 98013) et un autre message, cela fonctionnera immédiatement pour le 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
Mon approche:
Je commence par sous-classer UITextField et j'ajoute une propriété indexPath. Dans la méthode cellFor ... Méthode, je remets la propriété indexPath.
Puis j'ajoute le code suivant:
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);}];
à la textFieldShould/WillBegin ... etc.
Lorsque le clavier disparaît, vous devez l'inverser avec:
[UIView animateWithDuration:0.3 animations:^(void){self.tableView.contentOffset = CGPointMake(0, 0);}];
Si vous utilisez Three20
, utilisez la propriété autoresizesForKeyboard
. Il suffit de définir dans la méthode -initWithNibName:bundle
de votre contrôleur de vue
self.autoresizesForKeyboard = YES
Cela prend soin de:
Fait et fait.
LA BONNE RÉPONSE est la réponse de Sam Ho:
"Si vous utilisez UITableViewController au lieu de UIViewController, il le fera automatiquement.".
Assurez-vous simplement de connecter votre UITableView à la propriété TableView de UITableViewController (par exemple, ne l'ajoutez pas en tant que sous-vue de la propriété View de UITableViewController).
Veillez également à définir la propriété AutoresizingMask de votre UITableView sur FlexibleHeight.
Si vous utilisez une uitableview pour placer vos champs de texte ( de Jeff Lamarche ), vous pouvez simplement faire défiler la vue tableau en utilisant la méthode delegate comme ceci.
(Remarque: mes champs de texte sont stockés dans un tableau avec le même index que la ligne dans la table.)
- (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];
}
Les notifications au clavier fonctionnent, mais l'exemple de code d'Apple pour cela suppose que la vue de défilement est la vue racine de la fenêtre. Ce n'est généralement pas le cas. Vous devez compenser les barres d'onglets, etc., pour obtenir le bon décalage.
C'est plus facile qu'il n'y paraît. Voici le code que j'utilise dans un UITableViewController. Il a deux variables d'instance, hiddenRect et 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;
}
Utilisez la méthode UITextField's
delegate
:
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
}
- (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;
}
Une solution plus rationnelle. Il se glisse dans les méthodes de délégué UITextField afin qu'il ne soit pas nécessaire de modifier les notifications avec UIKeyboard.
Notes d'implémentation:
kSettingsRowHeight - la hauteur d'un UITableViewCell.
offsetTarget et offsetThreshold sont basés sur kSettingsRowHeight. Si vous utilisez une hauteur de ligne différente, définissez ces valeurs sur la propriété y du point. [alt: calcule le décalage de ligne d'une manière différente.]
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
CGFloat offsetTarget = 113.0f; // 3rd row
CGFloat offsetThreshold = 248.0f; // 6th row (i.e. 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;
}
J'ai rencontré quelque chose comme votre problème (je voulais un écran similaire aux paramètres de l'iPhone.app avec un tas de cellules modifiables empilées les unes sur les autres) et j'ai constaté que cette approche fonctionnait bien:
Puisque vous avez des champs de texte dans une table, la meilleure façon de le redimensionner est de redimensionner la table. Vous devez définir le paramètre tableView.frame pour qu’il soit plus petit en hauteur de la taille du clavier (environ 165 pixels), puis agrandissez-le à nouveau lorsque le clavier est licencié.
Vous pouvez également éventuellement désactiver l'interaction utilisateur pour la tableView à ce moment-là, si vous ne souhaitez pas que l'utilisateur fasse défiler l'écran.
Un exemple dans Swift, utilisant le point exact du champ de texte de Obtenir indexPath de UITextField dans UITableViewCell avec 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)
}
Fil de discussion très intéressant, j’ai également été confronté au même problème qui risque d’être plus grave parce que
Alors lisez les discussions ici et implémentez ma version, ce qui m’a aidé à remonter le contenu de mon iPad dans landscape mode . Voici du code (ce n’est pas infaillible et tout, mais cela a résolu mon problème) Vous devez d’abord avoir un délégué dans votre classe de cellule personnalisée. Celui-ci commence par la modification, il envoie le champ de texte à votre contrôleur de vue et définit le champ active = le champ de texte ici.
// MISE EN OEUVRE POUR GÉRER LE MODE PAYSAGE UNIQUEMENT
- (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];
}
}
// Appelé lorsque la notification UIKeyboardWillHideNotification est envoyée
- (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
Je viens de résoudre un tel problème par moi-même après avoir évoqué une multitude de solutions trouvées via Google et Stack Overflow.
Tout d’abord, assurez-vous que vous avez configuré un IBOutlet de votre UIScrollView, Ensuite, examinez de plus près/ Apple Doc: Gestion du clavier . Enfin, si vous pouvez faire défiler l’arrière-plan, mais le clavier couvre toujours les champs de texte, s'il vous plaît jeter un oeil à ce morceau de 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];
La principale différence entre cette pièce et celle d'Apple réside dans la condition if. Je crois que le calcul par Apple de la distance de défilement et de la condition selon laquelle les champs de texte couverts par le clavier ne sont pas exacts a été modifié comme ci-dessus.
Laissez-moi savoir si cela fonctionne
J'ai essayé à peu près la même approche et suis parvenu à un code plus simple et plus petit pour le même . J'ai créé un iTextView IBOutlet et associé à UITextView dans l'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];
}
Cela fonctionne parfaitement, et sur iPad aussi.
- (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];
}
J'ai créé Gist avec un ensemble de protocoles qui simplifie le travail d'ajout d'espace supplémentaire lorsque le clavier est affiché, masqué ou modifié.
Caractéristiques:
Exemple d'utilisation basique dans le contrôleur de vue contenant une vue de défilement (la vue de tableau est également prise en charge bien sûr).
class SomeViewController: UIViewController {
@IBOutlet weak var scrollView: UIScrollView!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
addKeyboardFrameChangesObserver()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
removeKeyboardFrameChangesObserver()
}
}
extension SomeViewController: ModifableInsetsOnKeyboardFrameChanges {
var scrollViewToModify: UIScrollView { return scrollView }
}
Le protocole KeyboardChangeFrameObserver
déclenchera un événement chaque fois que le cadre du clavier a été modifié (y compris l'affichage, le masquage, le changement de cadre).
addKeyboardFrameChangesObserver()
à viewWillAppear()
ou une méthode similaire.removeKeyboardFrameChangesObserver()
à viewWillDisappear()
ou une méthode similaire.Le protocole ModifableInsetsOnKeyboardFrameChanges
ajoute le support UIScrollView
au protocole principal. Il modifie les incrustations de la vue de défilement lorsque le cadre du clavier est modifié.
Votre classe doit définir la vue de défilement, les incrustations seront augmentées/diminuées lors des modifications du cadre du clavier.
var scrollViewToModify: UIScrollView { get }
Cette solution fonctionne pour moi, veuillez noter la ligne
[tableView setContentOffset:CGPointMake(0.0, activeField.frame.Origin.y-kbSize.height+160) animated:YES];
Vous pouvez changer la valeur 160 pour que cela fonctionne avec vous
- (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];
}
Ainsi, après des heures de travail exténuant à essayer d’utiliser ces solutions actuelles (et totalement défaillantes), j’ai finalement réussi à bien faire fonctionner les choses et à les mettre à jour pour utiliser les nouveaux blocs d’animation. Ma réponse est entièrement basée sur réponse d'Ortwin ci-dessus .
Donc, pour une raison quelconque, le code ci-dessus ne fonctionnait tout simplement pas pour moi. Ma configuration semblait assez semblable à celle des autres, mais peut-être parce que j'étais sur un iPad ou une version 4.3 ... aucune idée. Il faisait des calculs loufoques et tirait ma tableview de l'écran.
Voir le résultat final de ma solution: http://screencast.com/t/hjBCuRrPC (Veuillez ignorer la photo. :-P)
Alors, je suis allé avec l'essentiel de ce que faisait Ortwin, mais j'ai changé la façon dont il faisait des calculs pour ajouter la taille Origin.y & size.height de ma vue de table à la hauteur du clavier. Lorsque je soustrais la hauteur de la fenêtre de ce résultat, cela me dit combien d'intersection il y a. Si sa valeur est supérieure à 0 (il y a un certain chevauchement), j'effectue l'animation de la hauteur de l'image.
En outre, certains problèmes de résolution ont été résolus en 1) En attendant de faire défiler la cellule jusqu'à la fin de l'animation et 2) en utilisant l'option UIViewAnimationOptionBeginFromCurrentState lors du masquage du clavier.
Quelques choses à noter.
Encore une fois, je ne me serais pas approché de cette réponse si je n'avais pas fourni le cœur de la question à Ortwin. Voici le 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;
}
Si votre UITableView est géré par une sous-classe de UITableViewController et non par UITableView et que le délégué de champ de texte est UITableViewController, il doit gérer automatiquement tout le défilement - tous ces autres commentaires sont très difficiles à implémenter dans la pratique.
Pour un bon exemple, voir l'exemple de projet Apple: TaggedLocations.
Vous pouvez voir qu'il défile automatiquement, mais aucun code ne semble le faire. Ce projet comporte également des cellules de vue de tableau personnalisées. Par conséquent, si vous construisez votre application avec ce guide, vous devriez obtenir le résultat souhaité.
J'utilise ceux-ci et ils fonctionnent comme un charme:
BSKeyboardControls - BSKeyboardControls github
TPKeyboardAvoiding - TPKeyboardAvoiding github
Je l'utilise souvent dans mes projets. Cette solution fonctionne avec les vues de défilement, les vues de table ou les vues de collection et est facile à configurer . Elle relie également automatiquement les boutons «Suivant» du clavier pour faire défiler les champs de texte.
Vérifiez-le ici
Une autre méthode facile (ne fonctionne qu'avec une 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];
Petite variation avec Swift 4.2 ...
Sur UITableView, j’avais plusieurs sections mais je devais éviter l’effet d’en-tête flottant donc j’ai utilisé une approche "dummyViewHeight" comme on le voit quelque part ici sur Stack Overflow ... C’est donc mon solution pour ce problème (cela fonctionne aussi pour clavier + barre d'outils + suggestions):
Déclarez-la comme constante de classe:
let dummyViewHeight: CGFloat = 40.0
Ensuite
override func viewDidLoad() {
super.viewDidLoad()
//... some stuff here, not needed for this example
// Create non floating header
tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: dummyViewHeight))
tableView.contentInset = UIEdgeInsets(top: -dummyViewHeight, left: 0, bottom: 0, right: 0)
addObservers()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
removeObservers()
}
Et voici toute la magie ...
@objc func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
let keyboardHeight = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as AnyObject).cgRectValue.size.height
tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.bounds.size.width, height: dummyViewHeight))
tableView.contentInset = UIEdgeInsets(top: -dummyViewHeight, left: 0, bottom: keyboardHeight, right: 0)
}
}
@objc func keyboardWillHide(notification: NSNotification) {
UIView.animate(withDuration: 0.25) {
self.tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: self.tableView.bounds.size.width, height: self.dummyViewHeight))
self.tableView.contentInset = UIEdgeInsets(top: -self.dummyViewHeight, left: 0, bottom: 0, right: 0)
}
}
Solution facile et rapide.
Je viens de faire défiler vers la cellule droite chaque fois que le défilement se produit
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
En supposant que je sais que la table est maintenant dans ce mode "_keepMyCellOnTop" & Je connais la cellule sélectionnée "_selectedCellIndex" ou fais défiler jusqu'à la cellule sélectionnée
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (_keepMyCellOnTop)
{
[self.tableView scrollToRowAtIndexPath:_selectedCellIndex atScrollPosition:UITableViewScrollPositionTop animated:NO];
}
}
Cela empêchera le défilement.
Placer le code dans -(void) scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
entraînera un défilement vers le haut et le bas
UITableViewController
effectue le défilement automatiquement, en fait ... La différence par rapport à l'utilisation de UIViewController
est que vous devez créer Navbar-Buttonitems par programme à l'aide de NavigationController
, lorsque vous utilisez TableViewController
.
Je vais lancer ma solution (ou QuickDialog) dans le chapeau. En gros, attendez pour animer le défilement. Ce serait bien d’obtenir l’animation clavier JIT au lieu du nombre magique.
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
if (textField == self.emailTextField) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 50 * USEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
});
}
}
Voici comment j'ai fait ce travail, qui est un mélange de réponses de Sam Ho et Marcel W, et de certaines de mes propres corrections de bugs apportées à mon code de merde. J'utilisais un UITableViewController. La table est maintenant redimensionnée correctement lorsque le clavier est affiché.
1) Dans viewDidLoad
j'ai ajouté:
self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
2) J'avais oublié d'appeler les équivalents super
dans viewWillAppear
et awakeFromNib
. J'ai ajouté ces derniers dans.
dans viewdidload
-(void)viewdidload{
[super viewdidload];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChange:) name:UIKeyboardWillChangeFrameNotification object:nil];
}
-(void)keyboardWillChange:(NSNotification*)sender{
NSLog(@"keyboardwillchange sender %@",sender);
float margin=0 // set your own topmargin
CGFloat originY = [[sender.userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].Origin.y;
if (originY >= self.view.frame.size.height){
NSLog(@"keyboardclose");
[tb_ setFrame:CGRectMake(0, margin, self.view.frame.size.width, self.view.frame.size.height-margin)];
}else{
NSLog(@"keyobard on");
float adjustedHeight = self.view.frame.size.height - margin - (self.view.frame.size.height-originY);
[tb_ setFrame:CGRectMake(0, margin, self.view.frame.size.width, adjustedHeight)];
}
}
Regarde ma version :)
- (void)keyboardWasShown:(NSNotification *)aNotification
{
NSDictionary* info = [aNotification userInfo];
CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;
CGRect bkgndRect = cellSelected.superview.frame;
bkgndRect.size.height += kbSize.height;
[cellSelected.superview setFrame:bkgndRect];
[tableView setContentOffset:CGPointMake(0.0, cellSelected.frame.Origin.y-kbSize.height) animated:YES];
}
- (void)keyboardWasHidden:(NSNotification *)aNotification
{
[tableView setContentOffset:CGPointMake(0.0, 0.0) animated:YES];
}
Je pense que le meilleur moyen est d'utiliser UITableViewController.
Si vous voulez une UITableView dans un UIViewController , créez simplement un ContentView avec un UITableViewController incorporé et placez les lignes suivantes dans le viedDidLoad de l'UIViewController:
self.tableView = ((UITableViewController*)self.childViewControllers[0]).tableView;
self.tableView.delegate = self;
self.tableView.dataSource = self;
Facile ;)
Je viens de découvrir un autre bogue lors de l'utilisation de UITableViewController. Il ne défilait pas automatiquement lorsque le clavier s’affichait. J'ai remarqué que c'était à cause de contentInsetAdjustmentBehavior = .never sur UITableView.
Voici ma solution inspirée de l'écran "Édition d'événement" de l'application Calendrier iOS7.
L'un des points clés de cette solution est que le clavier est désactivé lorsque l'utilisateur fait défiler le tableau.
La mise en oeuvre:
1) Ajouter une propriété qui stockera le champ de texte sélectionné:
@property (strong) UITextField *currentTextField;
et la variable BOOL que nous utiliserons pour vérifier s’il faut masquer le clavier lorsque l’utilisateur fait défiler le tableau.
BOOL hideKeyboardOnScroll;
2) Gestion des rappels de délégués UITextField:
#pragma mark - UITextFieldDelegate
- (void) textFieldDidBeginEditing: (UITextField *) textField {
self.currentTextField = textField;
}
- (void) textFieldDidEndEditing: (UITextField *) textField {
self.currentTextField = nil;
}
- (BOOL) textFieldShouldReturn: (UITextField *) textField {
[textField resignFirstResponder];
CGPoint newContentOffset = CGPointZero;
if (tableView.contentSize.height > tableView.frame.size.height) {
newContentOffset.y = MIN(tableView.contentOffset.y, tableView.contentSize.height - tableView.frame.size.height);
}
[tableView setContentOffset: newContentOffset animated: YES];
return YES;
}
3) Gérez la méthode UIScrollViewDelegate pour vérifier la vue de défilement de cet utilisateur.
#pragma mark - UIScrollViewDelegate
- (void) scrollViewDidScroll: (UIScrollView *) scrollView {
if (hideKeyboardOnScroll == YES) {
[self.currentTextField resignFirstResponder];
}
}
4) Abonnez-vous aux notifications de clavier dans la méthode [viewWillAppear] de viewcontroller et désabonnez-vous à la méthode [viewWillDisappear].
- (void) viewWillAppear: (BOOL) animated {
[super viewWillAppear: animated];
[ [NSNotificationCenter defaultCenter] addObserver: self selector: @selector(keyboardWillShow:)
name: UIKeyboardWillShowNotification object: nil];
[ [NSNotificationCenter defaultCenter] addObserver: self selector: @selector(keyboardWillHide:)
name: UIKeyboardWillHideNotification object: nil];
}
- (void) viewWillDisappear: (BOOL) animated {
[super viewWillDisappear: animated];
[ [NSNotificationCenter defaultCenter] removeObserver: self name: UIKeyboardDidShowNotification object: nil];
[ [NSNotificationCenter defaultCenter] removeObserver: self name: UIKeyboardWillHideNotification object: nil];
}
5) Gérer les notifications du clavier:
- (void) keyboardWillShow: (NSNotification *) notification {
CGRect keyboardFrame = [ [ [notification userInfo] objectForKey: UIKeyboardFrameBeginUserInfoKey] CGRectValue];
// Find cell with textfield.
CGRect textFieldFrame = [tableView convertRect: self.currentTextField.frame fromView: self.currentTextField];
NSIndexPath *indexPath = [tableView indexPathForRowAtPoint: textFieldFrame.Origin];
UITableViewCell *cell = [tableView cellForRowAtIndexPath: indexPath];
//
// Shrink tableView size.
CGRect tableViewFrame = tableView.frame;
tableView.frame = CGRectMake(tableView.frame.Origin.x, tableView.frame.Origin.y, tableView.frame.size.width,
self.view.frame.size.height - tableView.frame.Origin.y - keyboardFrame.size.height);
//
// Check if cell is visible in shrinked table size.
BOOL cellIsFullyVisible = YES;
if ( cell.frame.Origin.y < tableView.contentOffset.y ||
(cell.frame.Origin.y + cell.frame.size.height) > (tableView.contentOffset.y + tableView.frame.size.height) ) {
cellIsFullyVisible = NO;
}
//
// If cell is not fully visible when scroll table to show cell;
if (cellIsFullyVisible == NO) {
CGPoint contentOffset = CGPointMake(tableView.contentOffset.x, CGRectGetMaxY(cell.frame) - tableView.frame.size.height);
if (cell.frame.Origin.y < tableView.contentOffset.y) {
contentOffset.y = cell.frame.Origin.y;
}
contentOffset.y = MAX(0, contentOffset.y);
// For some reason [setContentOffset] is called without delay then
// this code may not work for some cells. That why we call it with brief delay.
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[UIView animateWithDuration: 0.5 animations:^{
[tableView setContentOffset: contentOffset animated: NO];
} completion: ^(BOOL finished) {
hideKeyboardOnScroll = YES;
}];
});
} else {
hideKeyboardOnScroll = YES;
}
//
// Finally restore original table frame.
tableView.frame = tableViewFrame;
//
}
- (void) keyboardWillHide: (NSNotification *) notification {
[super keyboardWillHide: notification];
hideKeyboardOnScroll = NO;
}
Je viens de regarder à nouveau dans la référence de la librairie iOS 5.0 et trouve cette section intitulée "Déplacement de contenu situé sous le clavier": TextAndWebiPhoneOS KeyboardManagement
Est-ce nouveau depuis iOS 5, peut-être? Je n'ai pas encore lu, car je suis au milieu de quelque chose d'autre, mais peut-être que d'autres en savent plus et peuvent m'éclairer ainsi que d'autres ici.
Le document Apple annule-t-il les éléments abordés ici ou les informations fournies ici sont-elles toujours utiles aux utilisateurs du SDK iOS 5?
j'ai créé un petit projet qui résout ce problème avec le clavier. Dans mon cas, je n'ai qu'à faire en sorte que la vue de la table monte lorsque le clavier apparaît.
J'espère que cela t'aides!
Je pense qu'il n'y a pas de "bonne" façon de faire cela. Vous devez choisir la solution la mieux adaptée à votre cas d'utilisation. Dans mon application iPad, j'ai une UIViewController
présentée comme modale sous la forme UIModalPresentationFormSheet
et consistant en une UITableView
. Cette table contient deux UITextFields
par cellule. Le simple fait d'appeler scrollToRowAtIndexPath:atScrollPosition:animated:
dans la méthode textFieldDidBeginEditing:
ne fonctionne pas pour moi. J'ai donc créé une tableFooterView
:
- (void)viewDidLoad
{
[super viewDidLoad];
m_footerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, m_tableView.frame.size.width, 300.0f)];
[m_footerView setBackgroundColor:[UIColor clearColor]];
[m_tableView setTableFooterView:m_footerView];
[m_footerView release];
}
L'idée est que le clavier cache la tableFooterView
et non la UITextFields
. Donc, la tableFooterView
doit être suffisamment élevée. Après cela, vous pouvez utiliser scrollToRowAtIndexPath:atScrollPosition:animated:
dans la méthode textFieldDidBeginEditing:
.
Je pense qu'il est également possible d'afficher et de masquer la tableFooterView
de manière dynamique en ajoutant les observateurs pour les notifications au clavier, mais je ne l'ai pas encore essayé:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)keyboardWillShow:(NSNotification *)notification
{
[m_tableView setTableFooterView:m_footerView];
}
- (void)keyboardWillHide:(NSNotification *)notification
{
[m_tableView setTableFooterView:nil];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}
Solution pour Swift 3-4 avec animations et changement de cadre de clavier:
Tout d'abord, créez un Bool:
// MARK: - Private Properties
private var isKeyboardShowing = false
Deuxièmement, ajoutez des observateurs aux notifications du clavier système:
// MARK: - Overriding ViewController Life Cycle Methods
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: .UIKeyboardWillHide, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame), name: .UIKeyboardWillChangeFrame, object: nil)
}
Troisièmement, préparez la fonction d’animation:
func adjustTableViewInsets(keyboardHeight: CGFloat, duration: NSNumber, curve: NSNumber){
var extraHeight: CGFloat = 0
if keyboardHeight > 0 {
extraHeight = 20
isKeyboardShowing = true
} else {
isKeyboardShowing = false
}
let contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight + extraHeight, right: 0)
func animateFunc() {
//refresh constraints
//self.view.layoutSubviews()
tableView.contentInset = contentInset
}
UIView.animate(withDuration: TimeInterval(duration), delay: 0, options: [UIViewAnimationOptions(rawValue: UInt(curve))], animations: animateFunc, completion: nil)
}
Ajoutez ensuite les méthodes cible/action (appelées par les observateurs):
// MARK: - Target/Selector Actions
func keyboardWillShow(notification: NSNotification) {
if !isKeyboardShowing {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
let keyboardHeight = keyboardSize.height
let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber
let curve = notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber
adjustTableViewInsets(keyboardHeight: keyboardHeight, duration: duration, curve: curve)
}
}
}
func keyboardWillHide(notification: NSNotification) {
let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber
let curve = notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber
adjustTableViewInsets(keyboardHeight: 0, duration: duration, curve: curve)
}
func keyboardWillChangeFrame(notification: NSNotification) {
if isKeyboardShowing {
let duration = notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey] as! NSNumber
let curve = notification.userInfo?[UIKeyboardAnimationCurveUserInfoKey] as! NSNumber
if let newKeyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
let keyboardHeight = newKeyboardSize.height
adjustTableViewInsets(keyboardHeight: keyboardHeight, duration: duration, curve: curve)
}
}
}
Enfin, n'oubliez pas de supprimer les observateurs dans deinit ou dans viewWillDisappear:
deinit {
NotificationCenter.default.removeObserver(self)
}
// scroll tableview so content ends at the middle of the tableview (out of the way of the keyboard)
CGPoint newContentOffset = CGPointMake(0, [self.tableView contentSize].height - (self.tableView.bounds.size.height / 2));
[self.tableView setContentOffset:newContentOffset animated:YES];
Pas besoin de calculs, utilisez le code ci-dessous, il fonctionnera:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)}
func keyboardWillShow(_ notification:Notification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
}}
func keyboardWillHide(_ notification:Notification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
}}