Comment puis-je faire monter un UITextField lorsque le clavier est présent - au début de l'édition?


1692

Avec le SDK iOS:

J'ai un UIViewavec des UITextFields qui font apparaître un clavier. J'en ai besoin pour pouvoir:

  1. Permet de faire défiler le contenu du UIScrollViewpour voir les autres champs de texte une fois le clavier levé

  2. "Saut" automatique (en faisant défiler vers le haut) ou raccourcissement

Je sais que j'ai besoin d'un UIScrollView. J'ai essayé de changer la classe de mon UIViewen UIScrollViewmais je ne parviens toujours pas à faire défiler les zones de texte vers le haut ou vers le bas.

Ai-je besoin à la fois d'un UIViewet d'un UIScrollView? Est-ce que l'un entre dans l'autre?

Que faut-il mettre en œuvre pour faire défiler automatiquement le champ de texte actif?

Idéalement, autant de configuration des composants que possible se fera dans Interface Builder. Je voudrais seulement écrire du code pour ce qui en a besoin.

Remarque: le UIView(ou UIScrollView) avec lequel je travaille est affiché par une barre d'onglets ( UITabBar), qui doit fonctionner normalement.


Edit: j'ajoute la barre de défilement juste au moment où le clavier apparaît. Même si ce n'est pas nécessaire, j'ai l'impression que cela offre une meilleure interface, car l'utilisateur peut faire défiler et modifier les zones de texte, par exemple.

Je l'ai fait fonctionner où je change la taille d'image du UIScrollViewquand le clavier monte et descend. J'utilise simplement:

-(void)textFieldDidBeginEditing:(UITextField *)textField { 
    //Keyboard becomes visible
    scrollView.frame = CGRectMake(scrollView.frame.origin.x, 
                     scrollView.frame.origin.y, 
scrollView.frame.size.width,
scrollView.frame.size.height - 215 + 50);   //resize
}

-(void)textFieldDidEndEditing:(UITextField *)textField {
   //keyboard will hide
    scrollView.frame = CGRectMake(scrollView.frame.origin.x, 
       scrollView.frame.origin.y, 
     scrollView.frame.size.width,
      scrollView.frame.size.height + 215 - 50); //resize
}

Cependant, cela ne "monte" pas ou ne centre pas automatiquement les champs de texte inférieurs dans la zone visible, ce que j'aimerais vraiment.


6
Regarde ça. Pas de soucis pour vous. TPKeyboardAvoiding
Aruna

21
Il est documenté par Apple, je pense que c'est le meilleur moyen: developer.apple.com/library/ios/#documentation/StringsTextFonts/…
Maik639

58
Utilisez ce code. Vous avez juste besoin d'une ligne dans le fichier appdelegate.m et cela fonctionne. github.com/hackiftekhar/IQKeyboardManager
Pradeep Mittal

9
Le meilleur moyen que j'ai trouvé jusqu'à présent est ce TPKeyboardAvoiding
Mongi Zaidi

2
Une autre façon est d'ajouter de tels champs de texte de contenu et tout dans TableViewController et de laisser tableview gérer cela.
Vicky Dhas

Réponses:


1036
  1. Vous n'en aurez besoin que ScrollViewsi le contenu que vous avez maintenant ne rentre pas dans l'écran de l'iPhone. (Si vous ajoutez le ScrollViewcomme vue d'ensemble des composants juste pour faire TextFielddéfiler vers le haut lorsque le clavier apparaît, alors ce n'est pas nécessaire.)

  2. La manière standard d'empêcher les TextFields d'être couverts par le clavier est de déplacer la vue vers le haut / bas chaque fois que le clavier est affiché.

Voici un exemple de code:

#define kOFFSET_FOR_KEYBOARD 80.0

-(void)keyboardWillShow {
    // Animate the current view out of the way
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)keyboardWillHide {
    if (self.view.frame.origin.y >= 0)
    {
        [self setViewMovedUp:YES];
    }
    else if (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
    if ([sender isEqual:mailTf])
    {
        //move the main view, so that the keyboard does not hide it.
        if  (self.view.frame.origin.y >= 0)
        {
            [self setViewMovedUp:YES];
        }
    }
}

//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.3]; // if you want to slide up the view

    CGRect rect = self.view.frame;
    if (movedUp)
    {
        // 1. move the view's origin up so that the text field that will be hidden come above the keyboard 
        // 2. increase the size of the view so that the area behind the keyboard is covered up.
        rect.origin.y -= kOFFSET_FOR_KEYBOARD;
        rect.size.height += kOFFSET_FOR_KEYBOARD;
    }
    else
    {
        // revert back to the normal state.
        rect.origin.y += kOFFSET_FOR_KEYBOARD;
        rect.size.height -= kOFFSET_FOR_KEYBOARD;
    }
    self.view.frame = rect;

    [UIView commitAnimations];
}


- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    // register for keyboard notifications
    [[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];
    // unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillShowNotification
                                           object:nil];

    [[NSNotificationCenter defaultCenter] removeObserver:self
                                             name:UIKeyboardWillHideNotification
                                           object:nil];
}

3
Qu'est-ce que _textField? Je l'ai copié dans mon code, il dit que _textField n'est pas déclaré.
Cocoa Dev

C'est le champ que vous utilisez pour dire "lorsque l'utilisateur modifie ici la vue doit glisser vers le haut" ou quelque chose comme ça ... Cependant, vous pouvez le supprimer si, si vous avez plus de champs.
patrick

n'est-il pas difficile d'appeler - (void) setViewMovedUp: (BOOL) movesUp dans les événements keyBoardWillSHow et KeyBoardWillHide !!
Abduliam Rehmanius

4
Pas particulièrement utile si vous prenez en charge les rotations de la vue principale.
FractalDoctor du

2
Pour que cela fonctionne, j'ai dû commenter la textFieldDidBeginEditingsection.
avance

445

J'avais aussi beaucoup de problèmes avec la UIScrollViewcomposition de plusieurs UITextFields, dont un ou plusieurs seraient obscurcis par le clavier lors de leur édition.

Voici quelques éléments à considérer si votre UIScrollViewdéfilement n'est pas correct.

1) Assurez-vous que votre contentSize est supérieur à la UIScrollViewtaille du cadre. La façon de comprendre UIScrollViewsest que UIScrollViewc'est comme une fenêtre de visualisation sur le contenu défini dans contentSize. Donc, pour que le UIScrollviewpour faire défiler n'importe où, le contentSize doit être supérieur au UIScrollView. Sinon, aucun défilement n'est requis car tout ce qui est défini dans contentSize est déjà visible. BTW, contentSize par défaut = CGSizeZero.

2) Maintenant que vous comprenez que UIScrollViewc'est vraiment une fenêtre sur votre "contenu", la façon de vous assurer que le clavier n'obscurcit pas votre UIScrollView's"fenêtre" de visualisation serait de redimensionner le UIScrollViewafin que lorsque le clavier est présent, vous avez la UIScrollViewfenêtre dimensionné juste à l'image originale UIScrollViewframe.size.height moins la hauteur du clavier. Cela garantira que votre fenêtre n'est que cette petite zone visible.

3) Voici le problème: lorsque j'ai implémenté cela pour la première fois, j'ai pensé que je devrais obtenir le CGRectchamp de texte modifié et appeler la UIScrollView'sméthode scrollRecToVisible. J'ai implémenté la UITextFieldDelegateméthode textFieldDidBeginEditingavec l'appel à la scrollRecToVisibleméthode. Cela en fait travaillé avec un effet secondaire étrange que le défilement se casser la UITextFielden position. Pendant longtemps, je n'ai pas pu comprendre ce que c'était. Ensuite, j'ai commenté la textFieldDidBeginEditingméthode Delegate et tout fonctionne !! (???). En fin de compte, je pense que le UIScrollViewfait implicitement apporte implicitement le contenu actuellement édité UITextFielddans la fenêtre visible. Mon implémentation de la UITextFieldDelegateméthode et l'appel subséquent au scrollRecToVisibleétaient redondants et étaient à l'origine de l'étrange effet secondaire.

Voici donc les étapes pour faire défiler correctement votre UITextFielden un UIScrollViewlorsque le clavier apparaît.

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.

- (void)viewDidLoad 
{
    [super viewDidLoad];

    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillShow:) 
                                                 name:UIKeyboardWillShowNotification 
                                               object:self.view.window];
    // register for keyboard notifications
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(keyboardWillHide:) 
                                                 name:UIKeyboardWillHideNotification 
                                               object:self.view.window];
    keyboardIsShown = NO;
    //make contentSize bigger than your scrollSize (you will need to figure out for your own use case)
    CGSize scrollContentSize = CGSizeMake(320, 345);
    self.scrollView.contentSize = scrollContentSize;
}

- (void)keyboardWillHide:(NSNotification *)n
{
    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;


    // resize the scrollview
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height += (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];

    keyboardIsShown = NO;
}

- (void)keyboardWillShow:(NSNotification *)n
{
    // This is an ivar I'm using to ensure that we do not do the frame size adjustment on the `UIScrollView` if the keyboard is already shown.  This can happen if the user, after fixing editing a `UITextField`, scrolls the resized `UIScrollView` to another `UITextField` and attempts to edit the next `UITextField`.  If we were to resize the `UIScrollView` again, it would be disastrous.  NOTE: The keyboard notification will fire even when the keyboard is already shown.
    if (keyboardIsShown) {
        return;
    }

    NSDictionary* userInfo = [n userInfo];

    // get the size of the keyboard
    CGSize keyboardSize = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size;

    // resize the noteView
    CGRect viewFrame = self.scrollView.frame;
    // I'm also subtracting a constant kTabBarHeight because my UIScrollView was offset by the UITabBar so really only the portion of the keyboard that is leftover pass the UITabBar is obscuring my UIScrollView.
    viewFrame.size.height -= (keyboardSize.height - kTabBarHeight);

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationBeginsFromCurrentState:YES];
    [self.scrollView setFrame:viewFrame];
    [UIView commitAnimations];
    keyboardIsShown = YES;
}
  1. Inscrivez-vous aux notifications du clavier à viewDidLoad
  2. Se désinscrire pour les nofitications du clavier à viewDidUnload
  3. Assurez -vous que l' contentSizeest réglé et supérieur à votre UIScrollViewàviewDidLoad
  4. Réduction de la taille du UIScrollViewlorsque le clavier est présent
  5. Revenir le UIScrollViewlorsque le clavier disparaît.
  6. Utilisez un Ivar pour détecter si le clavier est déjà affiché à l'écran puisque les notifications de clavier sont envoyées chaque fois qu'un UITextFieldest , même si le onglets clavier est déjà présent pour éviter le rétrécissement du UIScrollViewquand il est déjà rétrécis

Une chose à noter est que le UIKeyboardWillShowNotificationse déclenchera même lorsque le clavier est déjà à l'écran lorsque vous tabulez sur un autre UITextField. J'ai pris soin de cela en utilisant un ivar pour éviter de redimensionner le UIScrollViewlorsque le clavier est déjà à l'écran. Redimensionner par inadvertance UIScrollViewle clavier quand il est déjà là serait désastreux!

J'espère que ce code vous évitera beaucoup de maux de tête.


3
Génial, mais deux problèmes: 1. UIKeyboardBoundsUserInfoKeyest obsolète. 2. keyboardSize est en "coordonnées d'écran", donc vos calculs viewFrame échoueront si le cadre est tourné ou mis à l'échelle.
Martin Wickman

21
@Martin Wickman - Utiliser à la CGSize keyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;place du dépréciéUIKeyboardBoundsUserInfoKey
sottenad

1
HI, j'ai fait la même chose, mais la vue texte ne monte que lorsque l'utilisateur commence à taper? Est-ce le comportement attendu ou je manque quelque chose?

3
[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].sizedevrait être [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size. Grande solution cependant!
j7nn7k

1
J'aime votre solution mais je pense que je peux la rendre encore plus simple: ne vous embêtez pas avec le truc de Notification Observer; appelez plutôt les bonnes routines d'animation dans les méthodes de délégué appropriées - pour UITextView, ce sont textViewDidBeginEditing et textViewDidEndEditing.
AlexChaffee

270

Il est préférable d'utiliser la mise en œuvre d'Apple, comme indiqué dans la documentation . Cependant, le code qu'ils fournissent est défectueux. Remplacez la partie trouvée keyboardWasShown:juste en dessous des commentaires par ce qui suit:

NSDictionary* info = [aNotification userInfo];
CGRect keyPadFrame=[[UIApplication sharedApplication].keyWindow convertRect:[[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue] fromView:self.view];
CGSize kbSize =keyPadFrame.size;
CGRect activeRect=[self.view convertRect:activeField.frame fromView:activeField.superview];
CGRect aRect = self.view.bounds;
aRect.size.height -= (kbSize.height);

CGPoint origin =  activeRect.origin;
origin.y -= backScrollView.contentOffset.y;
if (!CGRectContainsPoint(aRect, origin)) {
    CGPoint scrollPoint = CGPointMake(0.0,CGRectGetMaxY(activeRect)-(aRect.size.height));
    [backScrollView setContentOffset:scrollPoint animated:YES];
}

Les problèmes avec le code d'Apple sont les suivants: (1) Ils calculent toujours si le point est dans le cadre de la vue, mais c'est un ScrollView, il peut donc déjà avoir défilé et vous devez tenir compte de ce décalage:

origin.y -= scrollView.contentOffset.y

(2) Ils décalent le contentOffset de la hauteur du clavier, mais nous voulons le contraire (nous voulons décaler le contentOffsetde la hauteur visible à l'écran, pas ce qui ne l'est pas):

activeField.frame.origin.y-(aRect.size.height)

1
Dans les situations où la vue de défilement ne remplit pas l'écran, aRect doit être défini sur le cadre de la vue de défilement
mblackwell8

2
Ne devriez-vous pas vouloir que CGPoint origin = activeField.frame.origin + activeField.frame.size.height?, Parce que vous voulez que tout le champ de texte soit affiché et s'il se trouve que seuls quelques pixels sont visibles, alors le code n'entrera pas dans le état.
htafoya

1
Cette solution ne fonctionne pas en orientation Paysage - le champ de texte décolle du haut du port d'affichage. iPad avec iOS 7.1.
Andrew

4
Pour une meilleure prise en charge d'iOS 8, je suggère d'utiliser UIKeyboardFrameEndUserInfoKeyplutôt que d' UIKeyboardFrameBeginUserInfoKeyobtenir la taille du clavier, car cela permettra de détecter des modifications telles que le clavier personnalisé et d'activer / désactiver le texte prédictif.
Endareth

1
@Egor: Votre correctif le rend beaucoup plus efficace - mais la dernière ligne doit être inverse:self.scrollView.contentOffset = self.currentSVoffset;
Morten Holmgaard

244

Dans textFieldDidBeginEdittinget en textFieldDidEndEditingappelant la fonction [self animateTextField:textField up:YES]comme ceci:

-(void)textFieldDidBeginEditing:(UITextField *)textField 
{ 
    [self animateTextField:textField up:YES]; 
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField:textField up:NO];
}

-(void)animateTextField:(UITextField*)textField up:(BOOL)up
{
    const int movementDistance = -130; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? movementDistance : -movementDistance); 

    [UIView beginAnimations: @"animateTextField" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

J'espère que ce code vous aidera.

Dans Swift 2

func animateTextField(textField: UITextField, up: Bool) 
{
     let movementDistance:CGFloat = -130
     let movementDuration: Double = 0.3

     var movement:CGFloat = 0
     if up 
     {
         movement = movementDistance
     }
     else 
     {
         movement = -movementDistance
     }
     UIView.beginAnimations("animateTextField", context: nil)
     UIView.setAnimationBeginsFromCurrentState(true)
     UIView.setAnimationDuration(movementDuration)
     self.view.frame = CGRectOffset(self.view.frame, 0, movement)
     UIView.commitAnimations()
}


func textFieldDidBeginEditing(textField: UITextField) 
{
    self.animateTextField(textField, up:true)
}

func textFieldDidEndEditing(textField: UITextField) 
{
    self.animateTextField(textField, up:false)
}

SWIFT 3

 func animateTextField(textField: UITextField, up: Bool)
    {
        let movementDistance:CGFloat = -130
        let movementDuration: Double = 0.3

        var movement:CGFloat = 0
        if up
        {
            movement = movementDistance
        }
        else
        {
            movement = -movementDistance
        }
        UIView.beginAnimations("animateTextField", context: nil)
        UIView.setAnimationBeginsFromCurrentState(true)
        UIView.setAnimationDuration(movementDuration)
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
        UIView.commitAnimations()
    }


    func textFieldDidBeginEditing(textField: UITextField)
    {
        self.animateTextField(textField: textField, up:true)
    }

    func textFieldDidEndEditing(textField: UITextField)
    {
        self.animateTextField(textField: textField, up:false)
    }

1
pourquoi ne pas utiliser [UIView animateWithDuration: animations:^{ }];?
Andre Cytryn

2
cela fonctionne bien, bien que const int movementDistance = -130; // modifier au besoin doit être changé pour être plus flexible
Hammer

7
Incroyablement simple sur les petites implémentations. Pas de détournement avec ScrollViews et les problèmes de mise en page automatique ambigus.
James Perih

4
Euh ... vous n'utilisez pas du tout le paramètre textField. Pourquoi alors l'avoir comme paramètre de fonction? De plus, vous pouvez également utiliser l'opérateur ternaire dans Swift. Rend le code moins bavard.
stk

1
Si la couleur d'arrière-plan de la vue est autre chose que du noir, assurez-vous de définir la couleur de la fenêtre pour qu'elle corresponde à votre vue afin que l'utilisateur ne voie pas derrière. c'est-à-dire self.window.backgroundColor = [UIColor whiteColor];
bvmobileapps

134

En utilisant simplement TextFields:

1a) Utilisation Interface Builder: Sélectionnez Tous les TextFields => Modifier => Incorporer dans => ScrollView

1b) Incorporez manuellement TextFields dans UIScrollView appelé scrollView

2) Définir UITextFieldDelegate

3) Réglez chacun textField.delegate = self;(ou établissez des connexions Interface Builder)

4) Copier / coller:

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    CGPoint scrollPoint = CGPointMake(0, textField.frame.origin.y);
    [scrollView setContentOffset:scrollPoint animated:YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField {
    [scrollView setContentOffset:CGPointZero animated:YES];
}

8
Mais il remonte également la vue lorsqu'il textFieldest déjà visible.
TheTiger

1
Besoin de changer CGPointMake(0, textField.frame.origin.y);pourCGPointMake(0, textField.frame.origin.y + scrollView.contentInset.top);
Fury

@Egor Même après votre commentaire, cela ne fonctionne pas. Comme "TheTiger" l'a mentionné, il remonte la vue même après que le champ de texte soit visible.
rak appdev

Changement pour XCode 10: "Sélectionner tous les TextFields => Editeur => Incorporer => Vue de défilement"
tibalt

116

Pour Universal Solution , voici mon approche pour implémenter IQKeyboardManager .

entrez la description de l'image ici

Etape 1: - J'ai ajouté des notifications globales de UITextField, UITextViewet UIKeyboarddans une classe singleton. Je l'appelle IQKeyboardManager .

Étape 2: - Si trouvé UIKeyboardWillShowNotification, UITextFieldTextDidBeginEditingNotificationou UITextViewTextDidBeginEditingNotificationnotifications, j'essaye d'obtenir une topMostViewControllerinstance de la UIWindow.rootViewControllerhiérarchie. Afin de bien le découvrir UITextField/ UITextViewsur celui-ci, topMostViewController.viewle cadre doit être ajusté.

Étape 3: - J'ai calculé la distance de déplacement prévue de topMostViewController.viewpar rapport à la première réponse UITextField/ UITextView.

Étape 4: - Je me suis déplacé topMostViewController.view.framevers le haut / bas en fonction de la distance de déplacement attendue.

Étape5: - Si trouvé UIKeyboardWillHideNotification, UITextFieldTextDidEndEditingNotificationou UITextViewTextDidEndEditingNotificationnotification, j'essaie à nouveau d'obtenir une topMostViewControllerinstance de la UIWindow.rootViewControllerhiérarchie.

Étape 6: - J'ai calculé la distance perturbée topMostViewController.viewqui doit être restaurée à sa position d'origine.

Étape 7: - J'ai rétabli topMostViewController.view.frameen fonction de la distance perturbée.

Étape 8: - J'ai instancié l' instance de classe IQKeyboardManager singleton lors du chargement de l'application, de sorte que chaque UITextField/ UITextViewdans l'application s'ajustera automatiquement en fonction de la distance de déplacement attendue.

C'est tout ce que IQKeyboardManager fait pour vous avec AUCUNE LIGNE DE CODE vraiment !! il suffit de glisser-déposer le fichier source associé pour projeter. IQKeyboardManager prend également en charge l' orientation des périphériques , la gestion automatique de la barre d'outils UIT , KeybkeyboardDistanceFromTextField et bien plus que vous ne le pensez.


Ajoutez le répertoire IQKeyBoardManagerSwift à mon projet et ne fonctionne pas. Impossible d'activer cuz n'est pas reconnu dans AppDelegate ...
user3722523

2
cela ressemble à du phishing, la solution réelle n'est pas affichée, mais à la place, nous voyons une publicité pour ce compte GitHub.
Brian

101

Je l' ai mis en place un universel, sans rendez- vous UIScrollView, UITableViewet même UICollectionViewsous - classe qui prend soin de déplacer tous les champs de texte dans ce hors du chemin du clavier.

Lorsque le clavier est sur le point d'apparaître, la sous-classe trouvera la sous-vue qui est sur le point d'être modifiée et ajustera son décalage de cadre et de contenu pour s'assurer que la vue est visible, avec une animation correspondant à la fenêtre contextuelle du clavier. Lorsque le clavier disparaît, il restaure sa taille précédente.

Il devrait fonctionner avec pratiquement n'importe quelle configuration, soit une UITableViewinterface basée sur une interface, soit une interface composée de vues placées manuellement.

Voici: solution pour déplacer les champs de texte hors du chemin du clavier


Ça y est ...! C'est la solution la meilleure, la plus efficace et la plus parfaite! Il gère également les rotations correctement pour les vues de défilement. Si vous tournez, assurez-vous de redimensionner automatiquement verticalement, mais ne pas ancrer en bas. J'ai ajouté un UITextView à la vue de défilement dans mon cas. Merci des grappes!
Christopher

Très bon travail! Bien sûr, je suis paresseux en utilisant votre solution au lieu de la solution de bricolage, mais mon patron est plus heureux, alors oui! Même si quelqu'un veut se faire lui-même, j'aime votre approche de sous-classe, au lieu d'ajouter du code à chaque contrôleur. J'ai été choqué qu'iOS n'ait pas fait cela par défaut comme l'a fait Android - là encore, je trouve beaucoup de choses qui manquent dans iOS et MacOS :(
eselk

Est-ce que des choses étranges comme ma vue de défilement tiennent toutes dans l'écran, donc elles ne peuvent pas défiler. Après avoir ouvert et fermé le clavier, le contenu est désormais plus grand (on dirait que quelque chose d'invisible a été ajouté et non supprimé au bas de la page) et peut défiler.
Almo

91

Pour les programmeurs Swift :

Cela fera tout pour vous, mettez-les simplement dans votre classe de contrôleur de vue et implémentez le UITextFieldDelegateà votre contrôleur de vue et définissez le délégué de textField surself

textField.delegate = self // Setting delegate of your UITextField to self

Implémentez les méthodes de rappel des délégués:

func textFieldDidBeginEditing(textField: UITextField) {
    animateViewMoving(true, moveValue: 100)
}

func textFieldDidEndEditing(textField: UITextField) {
    animateViewMoving(false, moveValue: 100)
}

// Lifting the view up
func animateViewMoving (up:Bool, moveValue :CGFloat){
    let movementDuration:NSTimeInterval = 0.3
    let movement:CGFloat = ( up ? -moveValue : moveValue)
    UIView.beginAnimations( "animateView", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(movementDuration )
    self.view.frame = CGRectOffset(self.view.frame, 0,  movement)
    UIView.commitAnimations()
}

Pour Swift 4, 4.2, 5: Modifier

self.view.frame = CGRectOffset(self.view.frame, 0,  movement)

à

self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)

Dernière remarque sur cette implémentation: si vous poussez un autre contrôleur de vue sur la pile pendant que le clavier est affiché, cela créera une erreur où la vue reviendra à son cadre central mais le décalage du clavier ne sera pas réinitialisé. Par exemple, votre clavier est le premier répondeur de nameField, mais vous appuyez ensuite sur un bouton qui pousse votre Help View Controller sur votre pile. Pour corriger l'erreur de décalage, assurez-vous d'appeler nameField.resignFirstResponder () avant de quitter le contrôleur de vue, en vous assurant que la méthode déléguée textFieldDidEndEditing est également appelée. Je le fais dans la méthode viewWillDisappear.


3
SwiftLint n'a pas aimé self.view.frame = CGRectOffset(self.view.frame, 0, movement)donc j'ai changé cette ligne enself.view.frame.offsetInPlace(dx: 0, dy: movement)
levibostian

2
Swift 4 change self.view.frame = CGRectOffset (self.view.frame, 0, mouvement) en self.view.frame.offsetBy (dx: 0, dy: mouvement)
Asinox

FYI, pour que cela fonctionne, vous devez mettre. self.view.frame = self.view.frame.offsetBy (dx: 0, dy: mouvement)
Josh Wolff

64

Il y a déjà beaucoup de réponses, mais toujours aucune des solutions ci-dessus n'avait tout le nécessaire de positionnement sophistiqué requis pour une animation "parfaite" sans bug, rétrocompatible et sans scintillement. (bug lors de l'animation de frame / bounds et contentOffset ensemble, différentes orientations d'interface, clavier split iPad, ...)
Permettez-moi de partager ma solution:
(en supposant que vous avez configuré UIKeyboardWill(Show|Hide)Notification)

// Called when UIKeyboardWillShowNotification is sent
- (void)keyboardWillShow:(NSNotification*)notification
{
    // if we have no view or are not visible in any window, we don't care
    if (!self.isViewLoaded || !self.view.window) {
        return;
    }

    NSDictionary *userInfo = [notification userInfo];

    CGRect keyboardFrameInWindow;
    [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardFrameInWindow];

    // the keyboard frame is specified in window-level coordinates. this calculates the frame as if it were a subview of our view, making it a sibling of the scroll view
    CGRect keyboardFrameInView = [self.view convertRect:keyboardFrameInWindow fromView:nil];

    CGRect scrollViewKeyboardIntersection = CGRectIntersection(_scrollView.frame, keyboardFrameInView);
    UIEdgeInsets newContentInsets = UIEdgeInsetsMake(0, 0, scrollViewKeyboardIntersection.size.height, 0);

    // this is an old animation method, but the only one that retains compaitiblity between parameters (duration, curve) and the values contained in the userInfo-Dictionary.
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

    _scrollView.contentInset = newContentInsets;
    _scrollView.scrollIndicatorInsets = newContentInsets;

    /*
     * Depending on visual layout, _focusedControl should either be the input field (UITextField,..) or another element
     * that should be visible, e.g. a purchase button below an amount text field
     * it makes sense to set _focusedControl in delegates like -textFieldShouldBeginEditing: if you have multiple input fields
     */
    if (_focusedControl) {
        CGRect controlFrameInScrollView = [_scrollView convertRect:_focusedControl.bounds fromView:_focusedControl]; // if the control is a deep in the hierarchy below the scroll view, this will calculate the frame as if it were a direct subview
        controlFrameInScrollView = CGRectInset(controlFrameInScrollView, 0, -10); // replace 10 with any nice visual offset between control and keyboard or control and top of the scroll view.

        CGFloat controlVisualOffsetToTopOfScrollview = controlFrameInScrollView.origin.y - _scrollView.contentOffset.y;
        CGFloat controlVisualBottom = controlVisualOffsetToTopOfScrollview + controlFrameInScrollView.size.height;

        // this is the visible part of the scroll view that is not hidden by the keyboard
        CGFloat scrollViewVisibleHeight = _scrollView.frame.size.height - scrollViewKeyboardIntersection.size.height;

        if (controlVisualBottom > scrollViewVisibleHeight) { // check if the keyboard will hide the control in question
            // scroll up until the control is in place
            CGPoint newContentOffset = _scrollView.contentOffset;
            newContentOffset.y += (controlVisualBottom - scrollViewVisibleHeight);

            // make sure we don't set an impossible offset caused by the "nice visual offset"
            // if a control is at the bottom of the scroll view, it will end up just above the keyboard to eliminate scrolling inconsistencies
            newContentOffset.y = MIN(newContentOffset.y, _scrollView.contentSize.height - scrollViewVisibleHeight);

            [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
        } else if (controlFrameInScrollView.origin.y < _scrollView.contentOffset.y) {
            // if the control is not fully visible, make it so (useful if the user taps on a partially visible input field
            CGPoint newContentOffset = _scrollView.contentOffset;
            newContentOffset.y = controlFrameInScrollView.origin.y;

            [_scrollView setContentOffset:newContentOffset animated:NO]; // animated:NO because we have created our own animation context around this code
        }
    }

    [UIView commitAnimations];
}


// Called when the UIKeyboardWillHideNotification is sent
- (void)keyboardWillHide:(NSNotification*)notification
{
    // if we have no view or are not visible in any window, we don't care
    if (!self.isViewLoaded || !self.view.window) {
        return;
    }

    NSDictionary *userInfo = notification.userInfo;

    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:[[userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]];
    [UIView setAnimationCurve:[[userInfo valueForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]];

    // undo all that keyboardWillShow-magic
    // the scroll view will adjust its contentOffset apropriately
    _scrollView.contentInset = UIEdgeInsetsZero;
    _scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;

    [UIView commitAnimations];
}

Grandes améliorations de la réponse @Shiun. Mais une fois le clavier disparu, la vue ne revient pas en 1ère position. C'est toujours un excellent travail :)
Lucien

2
Merci, c'est la meilleure solution pour moi en 2017. Notez que vous n'avez pas besoin de suivre vous-même le focusControl, vous pouvez le déterminer avec UIApplication.shared.sendAction(...). Voici la version Swift 3 de votre réponse (moins la portion willHide), avec l' sendActionimplémentation: gist.github.com/xaphod/7aab1302004f6e933593a11ad8f5a72d
xaphod

@xaphod dans mon cas, j'avais besoin de concentrer plus de contrôles - par exemple un bouton sous un champ de saisie. mais oui, ce code a maintenant 4 ans et pourrait bénéficier d'améliorations.
Martin Ullrich

C'est probablement la bonne solution. La notification au clavier contient des données d'animation, les délégations de champ de texte ne connaissent pas la durée de l'animation, ce serait juste une supposition.
XY

62

Shiun a déclaré: "Il s'est avéré que l'UIScrollView amène implicitement l'UITextField actuellement édité dans la fenêtre visible implicitement" Cela semble être vrai pour iOS 3.1.3, mais pas 3.2, 4.0 ou 4.1. J'ai dû ajouter un scrollRectToVisible explicite afin de rendre l'UITextField visible sur iOS> = 3.2.


Ce n'est pas le UIScrollView qui fait défiler implicitement le UITextField édité en vue, c'est le UITextField qui appelle une [UITextField scrollTextFieldToVisibleIfNecessary]méthode privée qui à son tour appelle [UIScrollView scrollRectToVisible]quand [UITextField becomeFirstResponder]est appelé. Voir github.com/leopatras/ios_textfields_on_scrollview . Si les contraintes et les contrôleurs de vue sont correctement configurés, il n'est en fait pas nécessaire d'appeler scrollRectToVisibleexplicitement (au moins depuis IOS 11).
Leo

48

Une chose à considérer est de savoir si vous souhaitez utiliser un UITextFieldseul. Je n'ai rencontré aucune application iPhone bien conçue qui utilise réellement en UITextFieldsdehors de UITableViewCells.

Ce sera un travail supplémentaire, mais je vous recommande d'implémenter toutes les vues d'entrée de données une vue de table. Ajoutez un UITextViewà votre UITableViewCells.


1
L'une de mes applications doit permettre aux utilisateurs d'ajouter des notes de forme libre - alors oui, il est parfois utile d'utiliser un UITextField.
Peter Johnson

1
Je suis d'accord avec cette méthode. Zéro travail ou code de cette façon. Même si vous avez besoin d'une note de forme libre, vous pouvez toujours le faire avec une cellule de tableau
RJH

UITableViewest malheureusement la seule voie à suivre. Les notifications du clavier sont fragiles et ont changé au fil du temps. Exemple de code sur Stack Overflow: stackoverflow.com/a/32390936/218152
SwiftArchitect

Cette réponse est obsolète depuis environ cinq ans . La seule solution moderne est quelque chose comme ça ... stackoverflow.com/a/41808338/294884
Fattie

47

Ce document détaille une solution à ce problème. Regardez le code source sous «Déplacement de contenu situé sous le clavier». C'est assez simple.

EDIT: Remarqué il y a un petit problème dans l'exemple. Vous voudrez probablement écouter au UIKeyboardWillHideNotificationlieu de UIKeyboardDidHideNotification. Sinon, la vue de défilement derrière le clavier sera tronquée pendant la durée de l'animation de fermeture du clavier.


32

Solution la plus simple trouvée

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self animateTextField: textField up: YES];
}


- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField: textField up: NO];
}

- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
    const int movementDistance = 80; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? -movementDistance : movementDistance);

    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

L'écran monte même s'il n'est pas en bas. c'est-à-dire que si le champ de texte est en haut, il sort de l'écran. Comment contrôler ce cas?
MELWIN

@MELWIN Il suffit d'ajouter après cette ligne: int movement = (up ? -movementDistance : movementDistance); if (textField.frame.origin.y < self.view.frame.size.height - keyboard.height) { movementDistance = 0 }Veuillez keyboardnoter que la variable est le CGRect du clavier qui apparaît que vous obtenez en faisant:let keyboard = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey]!.CGRectValue())!
CapturedTree

31

Petit correctif qui fonctionne pour de nombreux UITextFields

#pragma mark UIKeyboard handling

#define kMin 150

-(void)textFieldDidBeginEditing:(UITextField *)sender
{
   if (currTextField) {
      [currTextField release];
   }
   currTextField = [sender retain];
   //move the main view, so that the keyboard does not hide it.
   if (self.view.frame.origin.y + currTextField.frame.origin. y >= kMin) {
        [self setViewMovedUp:YES]; 
   }
}



//method to move the view up/down whenever the keyboard is shown/dismissed
-(void)setViewMovedUp:(BOOL)movedUp
{
   [UIView beginAnimations:nil context:NULL];
   [UIView setAnimationDuration:0.3]; // if you want to slide up the view

   CGRect rect = self.view.frame;
   if (movedUp)
   {
      // 1. move the view's origin up so that the text field that will be hidden come above the keyboard 
      // 2. increase the size of the view so that the area behind the keyboard is covered up.
      rect.origin.y = kMin - currTextField.frame.origin.y ;
   }
   else
   {
      // revert back to the normal state.
      rect.origin.y = 0;
   }
   self.view.frame = rect;

   [UIView commitAnimations];
}


- (void)keyboardWillShow:(NSNotification *)notif
{
   //keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately

   if ([currTextField isFirstResponder] && currTextField.frame.origin.y + self.view.frame.origin.y >= kMin)
   {
      [self setViewMovedUp:YES];
   }
   else if (![currTextField isFirstResponder] && currTextField.frame.origin.y  + self.view.frame.origin.y < kMin)
   {
      [self setViewMovedUp:NO];
   }
}

- (void)keyboardWillHide:(NSNotification *)notif
{
   //keyboard will be shown now. depending for which textfield is active, move up or move down the view appropriately
   if (self.view.frame.origin.y < 0 ) {
      [self setViewMovedUp:NO];
   }

}


- (void)viewWillAppear:(BOOL)animated
{
   // register for keyboard notifications
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) 
                                                name:UIKeyboardWillShowNotification object:self.view.window]; 
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) 
                                                name:UIKeyboardWillHideNotification object:self.view.window]; 
}

- (void)viewWillDisappear:(BOOL)animated
{
   // unregister for keyboard notifications while not visible.
   [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 
}

rect.origin.y=+currTextField.frame.origin.yfonctionne bien merci
u.gen

30

Le code RPDP déplace avec succès le champ de texte hors du chemin du clavier. Mais lorsque vous faites défiler vers le haut après avoir utilisé et fermé le clavier, le haut a été déplacé hors de la vue. Cela est vrai pour le simulateur et l'appareil. Pour lire le contenu en haut de cette vue, il faut recharger la vue.

Son code suivant n'est-il pas censé faire baisser la vue?

else
{
    // revert back to the normal state.
    rect.origin.y += kOFFSET_FOR_KEYBOARD;
    rect.size.height -= kOFFSET_FOR_KEYBOARD;
}

23

Je ne sais pas si le déplacement de la vue vers le haut est la bonne approche, je l'ai fait de manière différente, en redimensionnant l'UIScrollView. Je l'ai expliqué en détail sur un petit article


Le lien vers l'article est mort.
Teo

22

Pour revenir à l'état d'affichage d'origine, ajoutez:

-(void)textFieldDidEndEditing:(UITextField *)sender

{
    //move the main view, so that the keyboard does not hide it.
    if  (self.view.frame.origin.y < 0)
    {
        [self setViewMovedUp:NO];
    }
}

20

Essayez cette petite astuce.

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    [self animateTextField: textField up: YES];
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    [self animateTextField: textField up: NO];
}

- (void) animateTextField: (UITextField*) textField up: (BOOL) up
{
    const int movementDistance = textField.frame.origin.y / 2; // tweak as needed
    const float movementDuration = 0.3f; // tweak as needed

    int movement = (up ? -movementDistance : movementDistance);

    [UIView beginAnimations: @"anim" context: nil];
    [UIView setAnimationBeginsFromCurrentState: YES];
    [UIView setAnimationDuration: movementDuration];
    self.view.frame = CGRectOffset(self.view.frame, 0, movement);
    [UIView commitAnimations];
}

19

Il y a tellement de solutions, mais j'ai passé quelques heures avant que ça marche. Donc, je mets ce code ici (il suffit de coller dans le projet, aucune modification n'est nécessaire):

@interface RegistrationViewController : UIViewController <UITextFieldDelegate>{
    UITextField* activeField;
    UIScrollView *scrollView;
}
@end

- (void)viewDidLoad
{
    [super viewDidLoad];

    scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame];

    //scrool view must be under main view - swap it
    UIView* natView = self.view;
    [self setView:scrollView];
    [self.view addSubview:natView];

    CGSize scrollViewContentSize = self.view.frame.size;
    [scrollView setContentSize:scrollViewContentSize];

    [self registerForKeyboardNotifications];
}

- (void)viewDidUnload {
    activeField = nil;
    scrollView = nil;
    [self unregisterForKeyboardNotifications];
    [super viewDidUnload];
}

- (void)registerForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShown:)
                                                 name:UIKeyboardWillShowNotification object:nil];

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

}

-(void)unregisterForKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillShowNotification
                                                  object:nil];
    // unregister for keyboard notifications while not visible.
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                    name:UIKeyboardWillHideNotification
                                                  object:nil];
}

- (void)keyboardWillShown:(NSNotification*)aNotification
{
    NSDictionary* info = [aNotification userInfo];
    CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    CGRect frame = self.view.frame;
    frame.size.height -= kbSize.height;
    CGPoint fOrigin = activeField.frame.origin;
    fOrigin.y -= scrollView.contentOffset.y;
    fOrigin.y += activeField.frame.size.height;
    if (!CGRectContainsPoint(frame, fOrigin) ) {
        CGPoint scrollPoint = CGPointMake(0.0, activeField.frame.origin.y + activeField.frame.size.height - frame.size.height);
        [scrollView setContentOffset:scrollPoint animated:YES];
    }
}

- (void)keyboardWillBeHidden:(NSNotification*)aNotification
{
     [scrollView setContentOffset:CGPointZero animated:YES];
}

- (void)textFieldDidBeginEditing:(UITextField *)textField
{
    activeField = textField;
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    activeField = nil;
}

-(BOOL) textFieldShouldReturn:(UITextField *)textField
{
    [textField resignFirstResponder];
    return YES;
}

PS: J'espère que le code aidera quelqu'un à produire rapidement l'effet souhaité. (Xcode 4.5)


Salut Hotjard, je reçois EXE_BAD_ACCESS dans le [self.view addSubview: natView];
Bala

18

@ user271753

Pour rétablir la vue d'origine, ajoutez:

-(BOOL)textFieldShouldReturn:(UITextField *)textField{
   [textField resignFirstResponder];
   [self setViewMovedUp:NO];
   return YES;
}

16

Il ne nécessite pas de vue de défilement pour pouvoir déplacer le cadre de vue. Vous pouvez modifier le cadre d'une viewcontroller'svue afin que la vue entière se déplace juste assez pour placer le champ de texte du premier répondant au-dessus du clavier. Lorsque j'ai rencontré ce problème, j'ai créé une sous-classe UIViewControllerqui fait cela. Il observe que le clavier apparaîtra une notification et trouve la sous-vue du premier répondant et (si nécessaire) il anime la vue principale vers le haut juste assez pour que le premier répondant soit au-dessus du clavier. Lorsque le clavier se cache, il anime la vue où elle était.

Pour utiliser cette sous-classe, faites de votre contrôleur de vue personnalisé une sous-classe de GMKeyboardVC et il hérite de cette fonctionnalité (assurez-vous simplement que vous implémentez viewWillAppearet viewWillDisappearqu'ils doivent appeler super). La classe est sur github .


Quelle licence? Certains de vos fichiers ont une licence open source et d'autres pas.
jaime

Attention: ce code n'est pas compatible avec les projets ARC.
Almo

Vous ajoutez simplement l'option de génération pour spécifier qu'il s'agit de fichiers non ARC ou vous êtes invités à les convertir en ARC et à soumettre une demande d'extraction.
progrmr

14

Rapide 4 .

Vous pouvez facilement monter et descendre UITextFieldOu UIViewAvec UIKeyBoardAvecAnimation entrez la description de l'image ici

import UIKit

class ViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var textField: UITextField!
    @IBOutlet var chatView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange), name: .UIKeyboardWillChangeFrame, object: nil)
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        textField.resignFirstResponder()
    }

    @objc func keyboardWillChange(notification: NSNotification) {

        let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
        let curFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        let targetFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        let deltaY = targetFrame.origin.y - curFrame.origin.y
        print("deltaY",deltaY)

        UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
            self.chatView.frame.origin.y+=deltaY // Here You Can Change UIView To UITextField
        },completion: nil)
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        textField.resignFirstResponder()
        return true
    }

}

2
Presque parfait. Sur iPhone X, vous obtenez un étrange écart entre le clavier et le champ de texte.
Houman

12

Voici la solution de piratage que j'ai trouvée pour une mise en page spécifique. Cette solution est similaire à la solution de Matt Gallagher en ce qu'elle fait défiler une section en vue. Je suis encore nouveau dans le développement d'iPhone et je ne connais pas le fonctionnement des mises en page. Ainsi, ce hack.

Mon implémentation devait prendre en charge le défilement lorsque vous cliquez dans un champ, ainsi que le défilement lorsque l'utilisateur sélectionne ensuite sur le clavier.

J'avais une UIView d'une hauteur de 775. Les commandes sont réparties essentiellement en groupes de 3 sur un grand espace. Je me suis retrouvé avec la disposition IB suivante.

UIView -> UIScrollView -> [UI Components]

Voici le hack

J'ai défini la hauteur UIScrollView à 500 unités plus grande que la disposition réelle (1250). J'ai ensuite créé un tableau avec les positions absolues dans lesquelles je dois faire défiler et une fonction simple pour les obtenir en fonction du numéro d'étiquette IB.

static NSInteger stepRange[] = {
    0, 0, 0, 0, 0, 0, 0, 0, 0, 140, 140, 140, 140, 140, 410
};

NSInteger getScrollPos(NSInteger i) {
    if (i < TXT_FIELD_INDEX_MIN || i > TXT_FIELD_INDEX_MAX) {
        return 0 ;
    return stepRange[i] ;
}

Maintenant, tout ce que vous avez à faire est d'utiliser les deux lignes de code suivantes dans textFieldDidBeginEditing et textFieldShouldReturn (cette dernière si vous créez une navigation de champ suivante)

CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
[self.scrollView setContentOffset:point animated:YES] ;

Un exemple.

- (void) textFieldDidBeginEditing:(UITextField *)textField
{
    CGPoint point = CGPointMake(0, getScrollPos(textField.tag)) ;
    [self.scrollView setContentOffset:point animated:YES] ;
}


- (BOOL)textFieldShouldReturn:(UITextField *)textField {

    NSInteger nextTag = textField.tag + 1;
    UIResponder* nextResponder = [textField.superview viewWithTag:nextTag];

    if (nextResponder) {
        [nextResponder becomeFirstResponder];
        CGPoint point = CGPointMake(0, getScrollPos(nextTag)) ;
        [self.scrollView setContentOffset:point animated:YES] ;
    }
    else{
        [textField resignFirstResponder];
    }

    return YES ;
}

Cette méthode ne fait pas «défiler vers l'arrière» comme le font d'autres méthodes. Ce n'était pas une exigence. Encore une fois, c'était pour une UIView assez «haute», et je n'ai pas eu de jours pour apprendre les moteurs de disposition internes.


12

Selon les documents , à partir d'iOS 3.0, la UITableViewControllerclasse redimensionne et repositionne automatiquement sa vue de table lorsqu'il y a une modification en ligne des champs de texte. Je pense qu'il ne suffit pas de mettre le champ de texte à l'intérieur d'un UITableViewCellcomme certains l'ont indiqué.

De la documentation :

Un contrôleur de vue de table prend en charge l'édition en ligne des lignes de vue de table; si, par exemple, les lignes comportent des champs de texte incorporés en mode édition, cela fait défiler la ligne en cours de modification au-dessus du clavier virtuel affiché.


J'ai trouvé le même commentaire. Oui c'est vrai. Ce qui est étrange, c'est qu'il fonctionne dans un UITabelViewController et pas dans un second. Mais je n'ai trouvé aucune différence dans ma mise en œuvre.
Morpheus78

11

Ici, j'ai trouvé la solution la plus simple pour gérer le clavier.

Vous devez simplement copier-coller ci-dessous l'exemple de code et modifier votre champ de texte ou toute vue que vous souhaitez déplacer vers le haut.

Étape 1

Copiez-collez juste en dessous de deux méthodes dans votre contrôleur

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

- (void)deregisterFromKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidHideNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

Étape 2

enregistrer et désenregistrer les notifications du clavier dans les méthodes viewWillAppear et viewWillDisappear respectivement.

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self registerForKeyboardNotifications];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [self deregisterFromKeyboardNotifications];
    [super viewWillDisappear:animated];
}

Étape 3

Voici la partie âme, remplacez simplement votre champ de texte et changez la hauteur à quel point vous voulez vous déplacer à l'envers.

- (void)keyboardWasShown:(NSNotification *)notification
{
    NSDictionary* info = [notification userInfo];
    CGSize currentKeyboardSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size;

    //you need replace your textfield instance here
    CGPoint textFieldOrigin = self.tokenForPlaceField.frame.origin;
    CGFloat textFieldHeight = self.tokenForPlaceField.frame.size.height;

    CGRect visibleRect = self.view.frame;
    visibleRect.size.height -= currentKeyboardSize.height;

    if (!CGRectContainsPoint(visibleRect, textFieldOrigin))
    {
        //you can add yor desired height how much you want move keypad up, by replacing "textFieldHeight" below

        CGPoint scrollPoint = CGPointMake(0.0, textFieldOrigin.y - visibleRect.size.height  + textFieldHeight); //replace textFieldHeight to currentKeyboardSize.height, if you want to move up with more height
        [self.scrollView setContentOffset:scrollPoint animated:YES];
    }
}

- (void)keyboardWillBeHidden:(NSNotification *)notification
{
    [self.scrollView setContentOffset:CGPointZero animated:YES];
}

Référence : eh bien, merci d'apprécier ce gars , qui a partagé cette belle coupure de code, une solution propre.

J'espère que ce serait sûrement utile à quelqu'un là-bas.


Je ne pense pas que ce soit le meilleur. Ithink @Dheeraj VS a raison: cela peut être fait facilement et automatiquement si ce champ de texte se trouve dans la cellule d'un tableau (même lorsque le tableau.scrollable = NO). REMARQUE : la position et la taille de la table doivent être raisonnables. Par exemple: - si la position y du tableau est comptée à 100 à partir du bas de la vue, alors le clavier de hauteur 300 chevauchera tout le tableau. - si la hauteur du tableau = 10, et que le champ de texte qu'il contient doit faire défiler vers le haut de 100 lorsque le clavier apparaît pour être visible, alors ce champ de texte sera hors de la limite du tableau.
samthui7

@ samthui7 La réponse Dheeraj ne fonctionne que si vous utilisez un TableViewController, pas seulement une table. Cela en fait une contrainte qui parfois ne convient pas.
Ben G

10

Vous cherchez un bon tutoriel pour les débutants sur le sujet, j'ai trouvé le meilleur tutoriel ici .

Dans l' MIScrollView.hexemple au bas du didacticiel, assurez-vous de mettre un espace à

@property (nonatomic, retain) id backgroundTapDelegate;

comme tu vois.


Salut savagenoob, merci pour le lien fourni et bienvenue sur stackoverflow. Veuillez essayer de fournir autant d'informations que possible lorsque vous répondez à de (futures) questions - les liens simples sont un peu fragiles. Cela dit, si la réponse est un lien vers un bon tutoriel qui pourrait être ignoré.
Maarten Bodewes

10

Quand UITextFieldest dans un UITableViewCelldéfilement doit être configuré automatiquement.

Si ce n'est pas le cas, c'est probablement à cause d'un code / d'une configuration incorrecte de la table.

Par exemple, lorsque j'ai rechargé ma longue table avec une UITextFielden bas comme suit,

-(void) viewWillAppear:(BOOL)animated
{
   [self.tableview reloadData];
}

puis mon champ de texte en bas a été obscurci par le clavier qui est apparu lorsque j'ai cliqué à l'intérieur du champ de texte.

Pour résoudre ce problème, je devais le faire -

-(void) viewWillAppear:(BOOL)animated
{
    //add the following line to fix issue
    [super viewWillAppear:animated];
    [self.tableview reloadData];
}

Je ne sais pas à quoi sert ce code? Lorsque le clavier s'affiche, viewWillAppearn'est pas appelé. Et reloadDatane rend pas visibles les lignes obscurcies.
Adam Johns

10

Utilisez ce tiers, vous n'avez pas besoin d'écrire une seule ligne

https://github.com/hackiftekhar/IQKeyboardManager

téléchargez le projet et glissez-déposez IQKeyboardManagervotre projet. Si vous trouvez un problème, veuillez lire le READMEdocument.

Les gars enlèvent vraiment son mal de tête pour gérer le clavier.


8

Remarque : cette réponse suppose que votre textField est dans un scrollView.

Je préfère traiter cela en utilisant scrollContentInset et scrollContentOffset au lieu de jouer avec les cadres de ma vue.

Écoutons d'abord les notifications du clavier

//call this from viewWillAppear
-(void)addKeyboardNotifications
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillHide:)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];
}
//call this from viewWillDisappear
-(void)removeKeyboardNotifications{
    [[NSNotificationCenter default
    Center] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil];
}

L'étape suivante consiste à conserver une propriété qui représente le premier répondeur actuel (UITextfield / UITextVIew qui possède actuellement le clavier).

Nous utilisons les méthodes déléguées pour définir cette propriété. Si vous utilisez un autre composant, vous aurez besoin de quelque chose de similaire.

Notez que pour textfield, nous le définissons dans didBeginEditing et pour textView dans shouldBeginEditing. C'est parce que textViewDidBeginEditing est appelé après UIKeyboardWillShowNotification pour une raison quelconque.

-(BOOL)textViewShouldBeginEditing:(UITextView * )textView{
    self.currentFirstResponder = textView;
    return YES;
}

-(void)textFieldDidBeginEditing:(UITextField *)textField{
    self.currentFirstResponder = textField;
}

Enfin, voici la magie

- (void)keyboardWillShow:(NSNotification*)aNotification{
    NSDictionary* info = [aNotification userInfo];
    CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];


    /*if currentFirstResponder is overlayed by the keyboard, move it so it bottom ends where the keyboard begins*/
    if(self.currentFirstResponder){

        //keyboard origin in currentFirstResponderFrame
        CGPoint keyboardOrigin = [self.currentFirstResponder convertPoint:kbFrame.origin fromView:nil];

        float spaceBetweenFirstResponderAndKeyboard = abs(self.currentFirstResponder.frame.size.height-keyboardOrigin.y);

        //only scroll the scrollview if keyboard overlays the first responder
        if(spaceBetweenFirstResponderAndKeyboard>0){
            //if i call setContentOffset:animate:YES it behaves differently, not sure why
            [UIView animateWithDuration:0.25 animations:^{
                [self.scrollView setContentOffset:CGPointMake(0,self.scrollView.contentOffset.y+spaceBetweenFirstResponderAndKeyboard)];
            }];
        }
    }

    //set bottom inset to the keyboard height so you can still scroll the whole content

    UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbFrame.size.height, 0.0);
    _scrollView.contentInset = contentInsets;
    _scrollView.scrollIndicatorInsets = contentInsets;

}

- (void)keyboardWillHide:(NSNotification*)aNotification{
    UIEdgeInsets contentInsets = UIEdgeInsetsZero;
    _scrollView.contentInset = contentInsets;
    _scrollView.scrollIndicatorInsets = contentInsets;
}

8

Ceci est la solution utilisant Swift.

import UIKit

class ExampleViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet var scrollView: UIScrollView!

    @IBOutlet var textField1: UITextField!
    @IBOutlet var textField2: UITextField!
    @IBOutlet var textField3: UITextField!
    @IBOutlet var textField4: UITextField!
    @IBOutlet var textField5: UITextField!

    var activeTextField: UITextField!

    // MARK: - View
    override func viewDidLoad() {
        super.viewDidLoad()
        self.textField1.delegate = self
        self.textField2.delegate = self
        self.textField3.delegate = self
        self.textField4.delegate = self
        self.textField5.delegate = self
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        self.registerForKeyboardNotifications()
    }

    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
        self.unregisterFromKeyboardNotifications()
    }

    // MARK: - Keyboard

    // Call this method somewhere in your view controller setup code.
    func registerForKeyboardNotifications() {
        let center:  NSNotificationCenter = NSNotificationCenter.defaultCenter()
        center.addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardDidShowNotification, object: nil)
        center.addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
    }

    func unregisterFromKeyboardNotifications () {
        let center:  NSNotificationCenter = NSNotificationCenter.defaultCenter()
        center.removeObserver(self, name: UIKeyboardDidShowNotification, object: nil)
        center.removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
    }

    // Called when the UIKeyboardDidShowNotification is sent.
    func keyboardWasShown (notification: NSNotification) {
        let info : NSDictionary = notification.userInfo!
        let kbSize = (info.objectForKey(UIKeyboardFrameBeginUserInfoKey)?.CGRectValue() as CGRect!).size

        let contentInsets: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height, 0.0);
        scrollView.contentInset = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;

        // If active text field is hidden by keyboard, scroll it so it's visible
        // Your app might not need or want this behavior.
        var aRect = self.view.frame
        aRect.size.height -= kbSize.height;
        if (!CGRectContainsPoint(aRect, self.activeTextField.frame.origin) ) {
            self.scrollView.scrollRectToVisible(self.activeTextField.frame, animated: true)
        }
    }

    // Called when the UIKeyboardWillHideNotification is sent
    func keyboardWillBeHidden (notification: NSNotification) {
        let contentInsets = UIEdgeInsetsZero;
        scrollView.contentInset = contentInsets;
        scrollView.scrollIndicatorInsets = contentInsets;
    }

    // MARK: -  Text Field

    func textFieldDidBeginEditing(textField: UITextField) {
        self.activeTextField = textField
    }

    func textFieldDidEndEditing(textField: UITextField) {
        self.activeTextField = nil
    }

}

Bonne réponse, mais je n'ai aucun problème lors de l'utilisation de TextField et TextView. De l'aide?
Thiha Aung du

@Thiha Aung, vos variables IBOutlet dans votre code source sont-elles connectées à l'IB?
Homam

Oui, ils sont également connectés.Juste avoir cette erreur lors de l'utilisation de UITextView sur cette ligne: if (! CGRectContainsPoint (aRect, self.activeTextField.frame.origin)) {
Thiha Aung

Signifie self.activeTextField est nul
Thiha Aung
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.