web-dev-qa-db-fra.com

Comment faire défiler la vue lorsque le clavier apparaît?

Je sais que cette question a été posée maintes et maintes fois, mais rien ne semble fonctionner pour moi. La plupart des solutions disponibles sont plutôt obsolètes, et les autres sont des blocs de code incroyablement énormes dix fois plus volumineux que le codage réel des projets. J'ai quelques UITextField alignés verticalement, mais lorsque le clavier se lance pour en modifier un, il recouvre le champ de texte. Je me demandais s'il existait un moyen simple pour les débutants de faire défiler la vue vers le haut, puis de revenir au début et à la fin de la modification.

Je vous remercie.

37
Henry F

J'ai mis au point des solutions qui fonctionnent avec les vues avec ou sans défilement en utilisant une notification au clavier et une détection du premier répondant actuel, mais j'utilise parfois cette solution triviale à la place: le moyen le plus simple consiste à détecter le clavier d'ouverture via la méthode textViewDidBeginEditing: du délégué de champ de texte et pour déplacer la vue entière vers le haut. Le moyen le plus simple de procéder consiste à changer le code self.view.bounds.Origin.y en -100 (ou peu importe). Utilisez la méthode textViewShouldEndEditing: correspondante pour définir la valeur opposée, à savoir 100 dans ce cas. Changer bounds est une procédure relative. Après modification, le cadre est déplacé mais les limites Origin sont toujours égales à zéro.

29
Peter DeWeese

Depuis que je l'ai trouvé, j'utilise TPKeyboardAvoiding - https://github.com/michaeltyson/TPKeyboardAvoiding .

Il fonctionne très bien et est très facile à installer:

  • Ajouter une UIScrollView dans le xib de votre contrôleur de vue
  • Définissez la classe de la vue de défilement sur TPKeyboardAvoidingScrollView (toujours dans xib, via l'inspecteur d'identité)
  • Placez tous vos contrôles dans cette vue de défilement

Vous pouvez également le créer par programme, si vous le souhaitez. 


Il existe une classe pour le même besoin dans un UITableViewController; il n'est nécessaire que si vous utilisez une version d'iOS inférieure à 4.3.

25
Guillaume

@BenLu et les autres utilisateurs confrontés à un problème de fonction ne sont jamais appelés à cause de la raison suivante:

 -(void)textFieldDidBeginEditing:(UITextField *)textField
{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.35f];
    CGRect frame = self.view.frame;
    frame.Origin.y = -100;
    [self.view setFrame:frame];
    [UIView commitAnimations];
}

-(void)textFieldDidEndEditing:(UITextField *)textField
{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.35f];
    CGRect frame = self.view.frame;
    frame.Origin.y = 100;
    [self.view setFrame:frame];
    [UIView commitAnimations];
}

J'ai passé quelque temps sur ce problème et rassemblé des morceaux de code pour créer une solution finale. Mon problème était lié au défilement UITableView et à l'ouverture/fermeture du clavier.

Vous avez besoin de deux méthodes partielles dans votre classe de cellule:

    void EditingBegin(UITextField sender)
    {
        // Height of tallest cell, you can ignore this!
        float tableMargin = 70.0f;
        float tableHeight = _tableView.Frame.Size.Height;
        float keyBoardHeight = KeyboardHeight();

        NSIndexPath[] paths = this._tableView.IndexPathsForVisibleRows;
        RectangleF rectLast = this._tableView.RectForSection(paths[paths.Length - 1].Section);
        RectangleF rectFirst = this._tableView.RectForSection(paths[0].Section);
        float lastCellY = rectLast.Y - rectFirst.Y;
        if (lastCellY > tableHeight - keyBoardHeight)
        {
            float diff = lastCellY - (tableHeight - tableMargin - keyBoardHeight);
            this._tableView.ContentInset = new UIEdgeInsets(0.0f, 0.0f, diff, 0.0f);
        }

        float cellPosition = this._tableView.RectForSection(this._section).Y;
        if (cellPosition > tableHeight - keyBoardHeight)
        {
            if (this._tableView.ContentInset.Bottom == 0.0f)
            {
                float diff = cellPosition - (tableHeight - tableMargin - keyBoardHeight);
                this._tableView.ContentInset = new UIEdgeInsets(0.0f, 0.0f, diff, 0.0f);
            }
            else
            {
                this._tableView.ScrollToRow(NSIndexPath.FromItemSection(0, this._section), UITableViewScrollPosition.Middle, true);
            }
        }
    }

    partial void EditingEnd(UITextField sender)
    {
        UIView.BeginAnimations(null);
        UIView.SetAnimationDuration(0.3f);
        this._tableView.ContentInset = new UIEdgeInsets(0.0f, 0.0f, 0.0f, 0.0f);
        UIView.CommitAnimations();
    }

puis dans votre classe de contrôleur de vue:

    public override void WillAnimateRotation(UIInterfaceOrientation toInterfaceOrientation, double duration)
    {
        base.WillAnimateRotation(toInterfaceOrientation, duration);

        float bottom = this.TableView.ContentInset.Bottom;
        if (bottom > 0.0f)
        {
            if (toInterfaceOrientation == UIInterfaceOrientation.Portrait || toInterfaceOrientation == UIInterfaceOrientation.PortraitUpsideDown)
            {
                bottom = bottom * UIScreen.MainScreen.Bounds.Width / UIScreen.MainScreen.Bounds.Height;
            }
            else
            {
                bottom = bottom * UIScreen.MainScreen.Bounds.Height / UIScreen.MainScreen.Bounds.Width;
            }

            UIEdgeInsets insets = this.TableView.ContentInset;
            this.TableView.ContentInset = new UIEdgeInsets(0.0f, 0.0f, bottom, 0.0f);
        }
    }  
1
U. Ali

Si vous avez une UITableView ou une UIScrollView, il est préférable de modifier les valeurs de contentOffset au lieu de modifier la frame

Travailler sur Peter's Answer , ajouter cette méthode à votre classe marche bien:

- (void)textViewDidBeginEditing:(UITextField *)textField {
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.35f];
    CGPoint offset = self.tableView.contentOffset;
    offset.y += 200; // You can change this, but 200 doesn't create any problems
    [self.tableView setContentOffset:offset];
    [UIView commitAnimations];
}

C'est ça, pas besoin d'ajouter la méthode textViewDidEndEditing.


Je ne devrais pas avoir besoin de le dire, mais pour que cela fonctionne, votre UITextField ou UITextViewdoit être un délégué de votre contrôleur.

0
Sheharyar

En commençant par la réponse de Peter, j'ai développé l'approche suivante dans Swift 3.0 sous iOS 10.1. Je fais cela pour un textView, alors j'ai implémenté les fonctions UITextViewDelegate textViewDidBeginEditing et textViewDidEndEditing où j'ajuste les limites de la vue. Comme vous pouvez le constater, je règle la valeur Origine Y sur un petit nombre positif pour défiler vers le haut, puis de nouveau sur 0 pour revenir à la position initiale. 

Voici le code correspondant de mon ViewController. Vous n'avez pas besoin d'animer, mais cela ajoute une touche agréable. 

func textViewDidBeginEditing(_ textView: UITextView)
{
    if UIScreen.main.bounds.height < 568 { 
        UIView.animate(withDuration: 0.75, animations: {
            self.view.bounds.Origin.y = 60
        })
    }
}

func textViewDidEndEditing(_ textView: UITextView)
{
    if UIScreen.main.bounds.height < 568 {
        UIView.animate(withDuration: 0.75, animations: {
            self.view.bounds.Origin.y = 0
        })
    }
}
0
Mario Hendricks