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


251

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'en ai un UITableViewqui est composé de cellules personnalisées. Les cellules sont constituées de 5 champs de texte côte à côte (un peu comme une grille).

Lorsque j'essaie de faire défiler et de modifier les cellules en bas de la UITableView, je n'arrive pas à positionner correctement mes cellules au-dessus du clavier.

J'ai vu de nombreuses réponses parler de la modification des tailles de vue, etc ... mais aucune n'a bien fonctionné jusqu'à présent.

Quelqu'un pourrait-il clarifier la "bonne" façon de procéder avec un exemple de code concret?


11
Cette documentation Applle décrit les étapes pour implémenter une solution à cette question. http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html
ChrisP

@ChrisP Ce lien indique qu'il n'a pas été mis à jour pour iOS 4.0
Bae


Suivez ci-dessous Url, cela fonctionnera: stackoverflow.com/questions/48922266/…
Venkatesh G

Réponses:


126

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


13
Avez-vous essayé de constater que cela ne fonctionnait pas? Ou la solution est-elle trop simple à croire pour vous? Étendez simplement UITableViewController au lieu de UIViewController et la cellule contenant les champs de texte défilera au-dessus du clavier chaque fois que les champs de texte deviendront le premier répondeur. Aucun code supplémentaire requis.
Sam Ho

3
Oui, mais en particulier sur l'iPad, nous avons besoin d'un moyen de le faire qui n'implique pas l'UITableViewController.
Bob Spryn

13
Pour clarifier, ce n'est pas une réponse raisonnable de dire que chaque fois que vous utilisez une table, elle doit être en plein écran, en particulier sur un iPad. Il existe des hordes d'exemples d'applications géniales qui ne font pas cela. Par exemple, beaucoup d'Apple, y compris l'application Contacts sur l'iPad.
Bob Spryn

32
Cela ne fonctionnera pas si vous remplacez [super viewWillAppear: YES]. A part ça, ça devrait marcher.
Rambatino

18
Si vous remplacez viewWillAppear: (BOOL) animated, n'oubliez pas d'appeler [super viewWillAppear: animated]; :)
Médéric Petit

93

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.


2
Et pourquoi pas?! Remplacez simplement UITableViewScrollPositionTop par UITableViewScrollPositionMiddle. Vous avez juste besoin de redimensionner UITableView pour ajuster la zone visible, bien sûr.
Mihai Damian

3
Ne semble pas fonctionner si un UITableViewController a pris en charge le redimensionnement de la vue de table lorsque le clavier est affiché: le contrôleur réduit la taille visible avec un contentInset, ce qui n'est apparemment pas pris en compte lors de la demande de visibleRowsou indexPathsForVisibleRows.
Julian D.

16
Ne fonctionne pas pour les dernières lignes de la vue tabulaire. Le clavier occulte toujours toutes les lignes qui ne peuvent pas défiler au-dessus du clavier.
Alex Zavatone

3
Pour que le comportement de défilement automatique fonctionne sur les dernières lignes du tableau, détectez le moment où ces lignes commencent à être modifiées et ajoutez un pied de page à la fin de la vue de tableau avec une vue vierge d'une certaine hauteur. Cela permettra à la vue de table de faire défiler les cellules au bon endroit.
Sammio2

10
Accéder à la cellule via une chaîne d'appels à la vue d'ensemble n'est pas fiable, à moins que vous ne vous assuriez réellement d'arriver à la cellule. Voir stackoverflow.com/a/17757851/1371070 et stackoverflow.com/a/17758021/1371070
Cezar

70

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

utiliser les notifications et obtenir la hauteur du clavier tout en intégrant l'orientation de l'appareil était génial, merci pour cela! la partie de défilement n'a pas fonctionné pour moi pour une raison quelconque, j'ai donc dû utiliser ceci:[tableView scrollToRowAtIndexPath: indexPath atScrollPosition: UITableViewScrollPositionMiddle animated: YES];
taber

7
C'est la meilleure réponse ici, je pense. Très propre. Seulement deux choses: 1) votre viewDidLoad n'appelle pas [super viewDidLoad] et 2) je devais avoir dans certaines mathématiques de la barre de tabulation sur les lignes frame.size.height. Sinon parfait! Merci.
toxaq

3
Voici la modification que toxaq décrit: MyAppDelegate *appDelegate = (MyAppDelegate*)[[UIApplication sharedApplication] delegate]; CGFloat tabBarHeight = appDelegate.tabBarController.tabBar.frame.size.height; Ensuite, soustrayez tabBarHeight de la hauteur du clavier partout où vous utilisez la hauteur du clavier.
Steve N

1
Si l'utilisateur tape sur le champ de texte, il fonctionne parfaitement. mais si l'utilisateur tape sur un autre champ de texte sans appuyer sur la touche retour, sa taille de table sera réduite.
Bhavin Ramani

1
@BhavinRamani a accepté. J'ai ajouté une simple propriété booléenne pour me souvenir si le clavier est déjà affiché ou non, et ignorer la réexécution du code lorsque cela n'est pas nécessaire.
Dirty Henry

46

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

Un détail mineur ... Utiliser Notificationau lieu de NSNotificationserait plus "Swift 3-y" :-)
Nicolas Miari

Cela vous aidera à repositionner s'il y a une barre de navigation - entourez UIView.animate avec ceci si let - if let frame = self.navigationController? .NavigationBar.frame {let y = frame.size.height + frame.origin.y}
Sean Dev

lorsque la rotation se produit, il y a un problème dans le chargement et une cellule disparaît lorsque la vue de la table défile manuellement
jothikenpachi

Bonne solution merci! Remarque - vous n'avez plus besoin de supprimer removeObserver.
Nick McConnell

44

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

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

Assurez-vous donc de mettre en œuvre comme ceci:

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

Et peu importe si vous utilisez UIViewControllerou UITableViewController; vérifié en mettant un UITableViewcomme une sous-vue de self.view dans le UIViewController. C'était le même comportement. La vue ne permettait pas de faire défiler si l'appel [super viewWillAppear:animated];manquait.


1
Cela a très bien fonctionné. Je me demandais pourquoi les gens disaient qu'UITableView le ferait pour moi et cela l'a résolu. Merci!
olivaresF

5
J'ai aussi eu ce problème, cette réponse devrait être au top!
Amiel Martin

J'ai perdu tellement de temps à essayer de comprendre par moi-même ... merci;)
budidino

+1 commençait à pleurer un peu, j'avais cette ligne mais il fallait aussi [tableViewController viewWillAppear: animated]; parce que j'ajoute un UITableViewController à un UIViewController. plus de larmes :)
colin lamarre

41

J'ai peut-être manqué cela, car je n'ai pas lu l'intégralité du message ici, mais ce que j'ai trouvé semble d'une simplicité trompeuse. Je n'ai pas mis cela à l'épreuve, testant dans toutes les situations, mais il semble que cela devrait fonctionner très bien.

ajustez simplement le contentInset de la table en fonction de 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 sûr

- (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 ça marche bien pour moi, mais comme je l'ai dit, je ne l'ai pas mis dans l'essoreuse ...


OMI, c'est la meilleure solution. La seule chose que je changerais est votre durée codée en dur pour[aNotification.userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue]
Andy

C'est très simple. Mais un problème que je trouve est qu'il n'animera pas le changement contentInsetet ne changera pas brusquement les limites du défilement.
Geek

Celui-ci a fonctionné le mieux pour moi, cependant, quelques problèmes. 1) Je ne sais pas où vous pouvez obtenir "currentField.indexPath", j'ai donc dû enregistrer le indexPath.row en tant que balise du champ et créer le indexPath plus tard. 2) Ne fonctionne pas pour les lignes en haut du tableau, il les fait défiler hors écran. J'ai dû ajouter du code pour faire défiler uniquement si l'indexPath de currentField est supérieur à ce qui peut tenir à l'écran. 3) a dû utiliser kbSize.Width (au lieu de height) sur iPad si c'est un paysage
Travis M.

désolé, on s'habitue tellement à notre propre code qu'on oublie parfois, hein? currentField est le champ de texte actuel avec lequel je travaille, et indexPath est une extension que j'ai ajoutée à la classe qui ajoute simplement un NSIndexPath pour que je sache dans quelle cellule il se trouve.
mickm

C'est la voie à suivre, ne pas déplacer les cadres simplement en modifiant les propriétés de la table.
Nextorlg

35

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

Tout d'abord, dans votre viewWillAppear: abonnez-vous aux notifications du clavier, afin que vous sachiez quand le clavier s'affichera et se cachera, et le système vous indiquera la taille du clavier, mais n'oubliez pas de vous désinscrire dans 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 les méthodes similaires à celles ci-dessous afin d'ajuster la taille de votre tableView pour qu'elle corresponde à la zone visible une fois que le clavier s'affiche. Ici, je surveille l'état du clavier séparément afin de pouvoir choisir moi-même le moment de remettre la tableView à sa pleine hauteur, car vous recevez ces notifications à chaque changement de champ. N'oubliez pas d'implémenter keyboardWillHide: et choisissez un endroit approprié pour fixer 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 sur quelques tailles, puis nous voyons où nous en sommes dans la zone visible, et définissons le rect que nous voulons faire défiler pour être soit la demi-vue au-dessus ou en dessous du milieu du champ de texte en fonction où il est dans la vue. Dans ce cas, nous avons un tableau de UITextFields et une énumération qui les garde, donc la multiplication de rowHeight par le numéro de ligne nous donne le décalage réel du cadre dans cette vue extérieure.

- (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.


Belle solution. Merci de l'avoir posté.
Alex Reynolds,

2
UIKeyboardBoundsUserInfoKeyest obsolète depuis iOS 3.2. Voir ma solution ci-dessous qui fonctionne sur toutes les versions iOS actuelles ≥ 3.0. / @ iPhoneDev
Ortwin Gentz

C'était plus compliqué que nécessaire. La réponse de @ user91083 était simple et fonctionne.
Richard Brightwell

1
Il y a un petit problème dans cette solution. keyboardWillShow est appelé APRÈS textFieldDidBeginEditing, donc quand nous voulons faire défiler jusqu'à une cellule, le cadre de tableView n'a pas encore changé, donc cela ne fonctionnera pas
HiveHicks

35

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 et pas seulement du UITableView.

Certaines des solutions présentées ici ne fonctionnent pas sur iOS ≥4, certaines ne fonctionnent pas sur iPad ou en mode paysage, certaines ne fonctionnent pas pour les claviers Bluetooth (où nous ne voulons pas de défilement), d'autres non fonctionne lors du basculement entre plusieurs champs de texte. Donc, si vous choisissez une solution, assurez-vous de tester ces cas. C'est la solution que nous utilisons utilisée 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" où vous pouvez tester les scénarios mentionnés ci-dessus.


Je ne sais pas s'il est utile d'utiliser des chaînes au lieu de constantes, car si Apple vient à l'idée de changer la chaîne en interne pour certaines raisons, votre solution ne fonctionne plus. De même, vous n'avez pas reçu d'avertissement lorsqu'il devient obsolète. Je pense

@iPortable: ce n'est pas idéal, je sais. Pouvez-vous suggérer une meilleure solution qui fonctionne sur toutes les versions ≥3.0?
Ortwin Gentz

1
Fonctionne comme un charme, mais pas pour UIInterfaceOrientationPortraitUpsideDown. Ensuite, le calcul de la réduction de la hauteur doit également être basé à l'envers: CGFloat ReduceHeight = keyboardRect.size.height - (CGRectGetMinY (viewRectAbsolute) - CGRectGetMinY (windowRect));
Klaas

Cela a des problèmes visuels très visibles sur mon iPad et le simulateur (4.3). Trop perceptible à utiliser. :(
Bob Spryn

J'aime que cette solution rende compte d'une barre d'outils en bas de l'écran.
pdemarest

24

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
    }

Fonctionne parfaitement, calculs minimaux nécessaires. J'ai ajouté du code qui restaure les encarts de table pour que cette réponse soit terminée.
Vitalii

Meilleure solution merci. J'ai
publié

Solution super parfaite jamais vue, j'en ai essayé d'autres mais a quelques problèmes. Votre solution fonctionne parfaitement sur iOS 10.2.
Wangdu Lin

8

J'espère que vous avez déjà trouvé une solution en lisant tout cela. Mais j'ai trouvé ma solution comme suit. Je m'attends à ce que vous ayez déjà une cellule avec UITextField. Ainsi, lors de la préparation, conservez simplement l'index de ligne dans la balise du champ de texte.

cell.textField.tag = IndexPath.row;

Créez une activeTextFieldinstance de UITextFieldavec une portée globale comme ci-dessous:

@interface EditViewController (){

    UITextField *activeTextField;

}

Donc, maintenant vous venez de copier coller mon code à la fin. Et n'oubliez pas d'ajouterUITextFieldDelegate

#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 du clavier Notifications:

Appelé lorsque le UIKeyboardDidShowNotificationest envoyé.

- (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 le UIKeyboardWillHideNotificationest envoyé

- (void)keyboardWillBeHidden:(NSNotification*)aNotification

{

    UIEdgeInsets contentInsets = UIEdgeInsetsZero;

    [self.tableView setContentInset:contentInsets];

    [self.tableView setScrollIndicatorInsets:contentInsets];

}

Maintenant, une chose reste, appelez la registerForKeyboardNotificationsméthode dans la ViewDidLoadméthode comme suit:

- (void)viewDidLoad {

    [super viewDidLoad];

    // Registering keyboard notification

    [self registerForKeyboardNotifications];

    // Your codes here...

}

Vous avez terminé, j'espère que votre textFieldsvolonté ne sera plus cachée par le clavier.


6

En combinant et en remplissant les blancs de plusieurs réponses (en particulier Ortwin Gentz, utilisateur 98013) et d'une autre publication, cela fonctionnera par défaut 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

J'ai bien utilisé ce code dans iOS 4.x, mais dans iOS5, il se bloque dans scrollToOldPosition car _topmostRowBeforeKeyboardWasShown est déjà libéré à ce moment-là. Je ne sais pas encore quelle est la solution. Souvenez-vous probablement de l'index au lieu de l'objet.
Thomas Tempelmann

5

Si vous utilisez une vue modifiable pour placer vos champs de texte ( de Jeff Lamarche ), vous pouvez simplement faire défiler la vue de table en utilisant la méthode déléguée comme ceci.

(Remarque: mes champs de texte sont stockés dans un tableau avec le même index que la ligne dans la vue 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];
    }

Vous ne mettez pas à jour le cadre tableView. Ensuite, les barres de défilement et le comportement de défilement sont incorrects lorsque le clavier est affiché. Voir ma solution.
Ortwin Gentz

5

Les notifications au clavier fonctionnent, mais l'exemple de code d'Apple 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 de tabulation, 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;
}

UIKeyboardCenterEndUserInfoKeyet UIKeyboardBoundsUserInfoKeysont obsolètes à partir d'iOS 3.2. Voir ma solution ci-dessous qui fonctionne sur toutes les versions iOS actuelles ≥ 3.0.
Ortwin Gentz

5

Si vous utilisez Three20, utilisez la autoresizesForKeyboardpropriété. Définissez simplement la -initWithNibName:bundleméthode de votre contrôleur de vue

self.autoresizesForKeyboard = YES

Cela prend en charge:

  1. Écoute des notifications du clavier et ajustement du cadre de la vue tabulaire
  2. Défilement jusqu'au premier répondant

Fait et fait.


qu'est-ce que Three20 ici? Pouvez-vous préciser cela?
Mubin Mall

5

Mon approche:

Je sous-classe d'abord UITextField et j'ajoute une propriété indexPath. Dans la méthode cellFor ... je remets la propriété indexPath.

J'ajoute ensuite 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);}];

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

4

Une solution plus rationalisée. Il se glisse dans les méthodes déléguées UITextField, donc il ne nécessite pas de messages avec les notifications UIKeyboard.

Notes de mise en œuvre:

kSettingsRowHeight - la hauteur d'un UITableViewCell.

offsetTarget et offsetThreshold sont supprimés de kSettingsRowHeight. Si vous utilisez une hauteur de ligne différente, définissez ces valeurs sur la propriété y du point. [alt: calculer 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;

}


4

Utilisez la UITextField's delegateméthode:

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 : %@",NSStringFromCGPoint(txtFieldPosition));
  NSIndexPath *indexPath = [yourTablViewHere indexPathForRowAtPoint:txtFieldPosition];

  if (indexPath != nil) {
     [yourTablViewHere scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
  }
  return YES;
}

Salut, je rencontre des problèmes pour que cela fonctionne sur Swift. Mes UITextFields connectés à UITableViewCell. Si j'implémente ce code dans mon UIViewController, je n'ai pas accès à UITextFields. Des idées?
Vetuka

4

Solution complète Swift 4.2

J'ai créé GIST avec un ensemble de protocoles qui simplifie le travail en ajoutant de l'espace supplémentaire lorsque le clavier est affiché, masqué ou modifié.

Caractéristiques :

  • Fonctionne correctement avec les changements de cadre du clavier (par exemple, les changements de hauteur du clavier comme emojii → clavier normal).
  • Prise en charge de TabBar et ToolBar pour l'exemple UITableView (dans d'autres exemples, vous recevez des encarts incorrects).
  • Durée d'animation dynamique (non codée en dur).
  • Approche orientée protocole pouvant être facilement modifiée à vos fins.

Usage

Exemple d'utilisation de base dans le contrôleur de vue qui contient une vue de défilement (la vue de table 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 }
}

Noyau: le cadre change d'observateur

Le protocole KeyboardChangeFrameObserverdéclenchera l'événement chaque fois que le cadre du clavier a été modifié (y compris l'affichage, le masquage, le changement de cadre).

  1. Appelez addKeyboardFrameChangesObserver()à viewWillAppear()ou méthode similaire.
  2. Appelez removeKeyboardFrameChangesObserver()à viewWillDisappear()ou méthode similaire.

Implémentation: vue défilante

ModifableInsetsOnKeyboardFrameChangesprotocole ajoute la UIScrollViewprise en charge au protocole principal. Il modifie les encarts de la vue de défilement lorsque le cadre du clavier est modifié.

Votre classe doit définir la vue de défilement, ses encarts seront augmentés / diminués lors des changements de cadre du clavier.

var scrollViewToModify: UIScrollView { get }

3

Puisque vous avez des champs de texte dans un tableau, le meilleur moyen est vraiment de redimensionner le tableau - vous devez définir le tableView.frame pour être plus petit en hauteur par la taille du clavier (je pense autour de 165 pixels), puis le développer à nouveau lorsque le clavier est fermé.

Vous pouvez également également désactiver l'interaction utilisateur pour la tableView à ce moment-là, si vous ne souhaitez pas que l'utilisateur défile.


J'appuie cela et je m'inscris à UIKeyboardWillShowNotification pour trouver la taille du clavier de manière dynamique.
benzado

Le nombre renvoyé par l'objet de notification ne fonctionne pas cependant. Ou du moins, ce n'était pas le cas en 2.2, le nombre renvoyé était incorrect et j'ai dû coder en dur la valeur 165 pour régler correctement la hauteur (elle était éteinte de cinq à dix pixels)
Kendall Helmstetter Gelner

2

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

Pourquoi différez-vous et utilisez-vous des cas spéciaux pour chaque champ de texte? Identifiez chaque champ de texte à partir du NSIndexPath de la cellule et changez cette méchante instruction if en 2 lignes de code. Vous voulez vraiment un appel cellForRowAtIndexPath, puis obtenez le textField de la cellule.
Alex Zavatone

Compte tenu de la façon dont cette situation est incroyablement floconneuse sur iOS, je pense qu'il est correct d'écrire du code "complètement déroulé, ridiculement littéral" pour cette situation.
Fattie

Considérant que cette réponse a été donnée il y a plus de 6 ans.
WrightsCS

2

J'ai essayé presque la même approche et j'ai trouvé un code plus simple et plus petit pour la même chose. J'ai créé un IBOutlet iTextView et associé à l'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];

    }

2

Donc, après des heures de travail exténuant à essayer d'utiliser ces solutions actuelles (et qui ont complètement échoué), j'ai finalement fait fonctionner les choses correctement et les ai mises à jour pour utiliser les nouveaux blocs d'animation. Ma réponse est entièrement basée sur la 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 similaire aux autres, mais peut-être parce que j'étais sur un iPad ou 4.3 ... aucune idée. Il faisait des calculs farfelus et tirait ma vue de table de l'écran.

Voir le résultat final de ma solution: http://screencast.com/t/hjBCuRrPC (veuillez ignorer la photo. :-P)

Je suis donc allé avec l'essentiel de ce que faisait Ortwin, mais j'ai changé la façon dont il faisait des calculs pour additionner origin.y & size.height de ma vue de table avec la hauteur du clavier. Lorsque je soustrais la hauteur de la fenêtre de ce résultat, cela me dit combien d'intersection j'ai en cours. Si sa valeur est supérieure à 0 (c'est-à-dire qu'il y a un certain chevauchement), j'exécute l'animation de la hauteur du cadre.

De plus, il y a eu quelques problèmes de rafraîchissement qui ont été résolus en 1) attendant de faire défiler jusqu'à la cellule jusqu'à ce que l'animation soit terminée et 2) en utilisant l'option UIViewAnimationOptionBeginFromCurrentState lors du masquage du clavier.

Quelques choses à noter.

  • _topmostRowBeforeKeyboardWasShown & _originalFrame sont des variables d'instance déclarées dans l'en-tête.
  • self.guestEntryTableView est ma tableView (je suis dans un fichier externe)
  • IASKCGRectSwap est la méthode d'Ortwin pour inverser les coordonnées d'un cadre
  • Je ne mets à jour la hauteur de la table que si au moins 50px vont être affichés
  • Comme je ne suis pas dans un UIViewController, je n'ai pas self.view, donc je remets simplement la tableView à son cadre d'origine

Encore une fois, je ne me serais pas approché de cette réponse si je Ortwin n'en avait pas fourni l'essentiel. 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;
}

Ajout de ma fonction FixOriginRotation qui corrige le système de coordonnées de la vue avant de mettre à jour son cadre, etc. Je pense que cela fait partie des raisons pour lesquelles j'ai eu des problèmes au début. Je ne savais pas que le système de coordonnées de fenêtre iOS a pivoté avec l'appareil!
Bob Spryn

2

Ce soluton fonctionne pour moi, veuillez noter la ligne

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

Vous pouvez modifier la valeur 160 pour l'adapter à votre travail

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

2

Fil de discussion très intéressant, j'ai également fait face au même problème peut être pire parce que

  1. J'utilisais une cellule personnalisée et le champ de texte était à l'intérieur.
  2. J'ai dû utiliser UIViewController pour répondre à mes exigences, je ne peux donc pas profiter de UITableViewController.
  3. J'avais des critères de filtrage / tri dans ma cellule de tableau, c.-à-d. Que les cellules ur continuent de changer et de garder une trace du chemin d'index et tout ne va pas aider.

Alors lisez les discussions ici et implémentez ma version, ce qui m'a aidé à remonter mon contenu sur iPad en mode paysage . Voici le code (ce n'est pas indéréglable et tout, mais cela a résolu mon problème).

// MISE EN ŒUVRE 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 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


2

Je viens de résoudre un tel problème par moi-même après avoir référé une masse de solutions trouvées via Google et Stack Overflow.

Tout d'abord, veuillez vous assurer que vous avez configuré une sortie IBO de votre UIScrollView, puis veuillez examiner de près Apple Doc: Gestion des claviers . Enfin, si vous pouvez faire défiler l'arrière-plan, mais que le clavier couvre toujours les champs de texte, jetez un œil à 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 Apple réside dans la condition if. Je crois que le calcul par Apple de la distance de défilement et de la condition que le champ de texte couvert par le clavier ne soit pas précis, j'ai donc fait ma modification comme ci-dessus.

Dites moi si ca marche


2

Un exemple dans Swift, en utilisant le point exact du champ de texte de Get 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)
}

1

Une autre méthode simple (ne fonctionne qu'avec une seule 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];

1

Si votre UITableView est géré par une sous-classe de UITableViewController et non UITableView, et que le délégué du champ de texte est UITableViewController, il devrait 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 de code Apple: TaggedLocations.

Vous pouvez voir qu'il défile automatiquement, mais il ne semble pas y avoir de code qui le fasse. Ce projet a également des cellules de vue de tableau personnalisées, donc si vous construisez votre application avec comme guide, vous devriez obtenir le résultat souhaité.


1

Voici comment j'ai fait ce travail, qui est un mélange des réponses de Sam Ho et Marcel W, et quelques-unes de mes propres corrections de bugs apportées à mon code merdique. J'utilisais un UITableViewController. Le tableau se redimensionne désormais correctement lorsque le clavier est affiché.

1) Dans viewDidLoadj'ai ajouté:

self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

2) J'avais oublié d'appeler les superéquivalents en viewWillAppearet awakeFromNib. Je les ai rajoutés.


1

UITableViewControllerfait le défilement automatiquement, en effet. La différence par rapport à l'utilisation d'un UIViewControllerest que vous devez créer des éléments Navbar-Buttonitems par programme en utilisant le NavigationController, lorsque vous utilisez un TableViewController.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.