web-dev-qa-db-fra.com

InputAccessoryView ancré en bas

J'essaie d'obtenir un comportement de positionnement similaire à la barre de saisie de texte inférieure dans l'application Messages d'Apple.

J'ai essayé de nombreuses approches, recherché haut et bas et il y a beaucoup de questions similaires mais aucune n'a été satisfaisante.

Spécifier:

  1. Il y a un UIToolbar en bas de la vue
  2. La barre d'outils doit suivre le clavier lorsque le clavier apparaît et disparaît
  3. La barre d'outils doit rester au-dessus du clavier lorsque le clavier est visible
  4. Lorsque le clavier est masqué, la barre d'outils reste ("ancrée") au bas de la vue

Les solutions proposées:

Animez manuellement la barre d'outils en réponse aux notifications d'apparence du clavier

Cette solution ne répond pas à un cas particulier de la deuxième exigence ( la barre d'outils est de suivre le clavier tel que le clavier apparaît et disparaît ):

  • Dans iOS 7, UIScrollViewKeyboardDismissMode a été introduit. Il permet un geste interactif pour ignorer le clavier. Au fur et à mesure que l'utilisateur parcourt le bord supérieur du clavier, le cadre du clavier suit progressivement. Cette solution ne fait rien pour s'adapter à ce comportement et laisse simplement la barre d'outils bloquée à sa position animée.

De plus, cette solution ne répond pas non plus à un cas particulier de la troisième condition ( la barre d'outils doit rester au-dessus du clavier lorsque le clavier est visible ) :

  • Rotation. Cette solution nécessite un code supplémentaire, extrêmement ennuyeux (comme nous le verrons dans la prochaine solution proposée) pour faire pivoter la barre d'outils en réponse à la rotation de l'appareil.

Un autre problème avec cette solution:

  • Hauteur du clavier. Avec cette solution, la barre d'outils n'est pas supposée faire partie de la hauteur du clavier, donc du code supplémentaire doit être écrit pour prendre en charge l'insertion correcte du contenu.

Prochaine solution proposée:

Utilisez le UIResponder de inputAccessoryView

Cette solution semble être la manière Apple destinée à prendre en charge ce type de comportement, car elle résout tous les défauts de l'animation manuelle de la barre d'outils. Mais cette solution manque complètement la quatrième condition ( lorsque le clavier est masqué, la barre d'outils reste ("ancrée") en bas de la vue ).


Il semble que la solution consiste à utiliser le UIResponder de inputAccessoryView, mais en faisant en sorte que le inputAccessoryView ne se déplace pas sous la vue. Je cherche du code propre pour accomplir cela. Il y a des tentatives élaborées, presque nobles ailleurs, mais comme mentionné, elles ne répondent pas aux exigences.

Dans mon cas particulier, je cherche à utiliser la barre d'outils de UINavigationController qui entraîne des problèmes supplémentaires car ce n'est pas un comportement prévu pour UINavigationController. Peu importe, je suis prêt à introduire des correctifs hacky pour y parvenir.

46
Stian Høiland

On vient de me montrer "la" solution par Jason Foreman (@threeve). Sur votre contrôleur de vue (oui, contrôleur de vue), ajoutez inputAccessoryView: et retournez la vue que vous souhaitez ancrer en bas et déplacez-vous avec le clavier. Ça marche juste. La vue n'a pas besoin d'être dans votre hiérarchie de vues, elle sera insérée automatiquement par le contrôleur de vue.

edit: implémentez également canBecomeFirstResponder et retournez YES (comme l'a noté Max Seelemann). reloadInputViews peut également être utile.

57
Jonathan Badeen

La solution susmentionnée de Jonathan Badeen solution a fonctionné pour moi. Voici le code de Arik montrant comment l'implémenter (cela devrait aller dans votre contrôleur de vue approprié):

    - (BOOL)canBecomeFirstResponder{

        return YES;

    }

    - (UIView *)inputAccessoryView{

        return self.inputAccessoryToolbar;  
     // where inputAccessoryToolbar is your keyboard accessory view

    }
31
Russ Hooper

Pour ceux qui recherchent Swift version:

Connectez votre barre d'outils (dans mon cas 'myToolBar') à votre contrôleur de vue. Remplacez ensuite la méthode canBecomeFirstResponder et remplacez la variable getter inputAccessoryView. N'oubliez pas non plus d'ajouter la self.myToolBar.removeFromSuperview() sinon xcode se plaindra.

class ViewController: UIViewController {

    @IBOutlet var myToolBar: UIToolbar!

    override func canBecomeFirstResponder() -> Bool {
        return true
    }


    override var inputAccessoryView:UIView{
        get{
            return self.myToolBar
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.myToolBar.removeFromSuperview()
    }
}
9
jonprasetyo

Il existe une excellente solution open-source facile à implémenter de la part des bons employés de Slack: SlackTextViewController.

Voici comment implémenter une vue avec une barre d'outils ancrée en quatre étapes:

  1. Installez le pod dans votre projet. ( http://cocoapods.org/?q=SlackTextViewController )
  2. Si vous écrivez une application dans Swift, créez un en-tête pour faire le pont entre votre Swift et leurs classes Obj-C. Voici une procédure pas à pas rapide à ce sujet . (L'en-tête de pontage ne sera pas nécessaire une fois les classes traduites en Swift, quelqu'un veut-il collaborer à cela?)
  3. Créez un MessageViewController qui hérite de SLKTextViewController, pas besoin d'écrire plus de code que celui-ci:

    import Foundation
    import UIKit
    
    class MessageViewController: SLKTextViewController {
    
        required init(coder aDecoder: NSCoder!) {
            super.init(coder: aDecoder)
        }
    }
    
  4. Dans Storyboard, créez un contrôleur de vue qui hérite de MessageViewController.
  5. Testez l'application sur un appareil ou un émulateur, vous verrez une belle barre de texte ancrée qui se développe également (en bonus) lorsque l'utilisateur écrit des lignes de texte supplémentaires.

Accessoires à l'équipe de Slack pour l'extraction d'un pod aussi utile.

6
Alex Soble

Je sais donc que c'est un ancien message et je ne sais pas si vous avez résolu cela ou non, mais j'ai trouvé un moyen de faire fonctionner cela. Je crois qu'il y a un bogue dans inputAccessoryView, mais j'ai créé une solution hacky pour se comporter comme le fait l'application de messages. Je pense avoir fourni tout le code nécessaire pour implémenter le travail. Je vais essayer de créer un article de blog plus approprié dans un proche avenir, avec une description plus approfondie de mes résultats. Toutes les questions, faites-le moi savoir.

@property(nonatomic,assign)BOOL isFirstKeyboard; //workaround for keyboard bug

@property(nonatomic,assign)BOOL isViewAppear;

@property(nonatomic,strong)ChatBarView *chatView; //custom chat bar view

@property(nonatomic,strong)UIView *footerPadView; //just to add some Nice padding
////////////////////////////////////////////////////////////////////////////////////////////////////
//in the view did load
- (void)viewDidLoad
{
    //more app specific code...
    self.tableView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;
    self.chatView.textView.inputAccessoryView = self.chatView;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
-(void)done
{
    self.isFirstKeyboard = YES;
    [self.chatView.textView becomeFirstResponder];
}
////////////////////////////////////////////////////////////////////////////////////////////////////
- (void) moveTextViewForKeyboard:(NSNotification*)aNotification up:(BOOL)up
{
    if(!self.isViewAppear)
        return;
    //NSLog(@"keyboard is: %@", up ? @"UP" : @"Down");
    if(!up && !self.isFirstKeyboard)
        [self performSelector:@selector(done) withObject:nil afterDelay:0.01];
    else if(!up & self.isFirstKeyboard)
    {
        self.isFirstKeyboard = NO;
        [self.view addSubview:self.chatView];
        CGRect frame = self.chatView.frame;
        frame.Origin.y = self.view.frame.size.height - self.chatView.frame.size.height;
        self.chatView.frame = frame;
    }
    else
    {
        NSDictionary* userInfo = [aNotification userInfo];
        NSTimeInterval animationDuration;
        UIViewAnimationCurve animationCurve;
        CGRect keyboardEndFrame;

        [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] getValue:&animationCurve];
        [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] getValue:&animationDuration];
        [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] getValue:&keyboardEndFrame];

        // Animate up or down
        [UIView beginAnimations:nil context:nil];
        if(up)
            [UIView setAnimationDuration:0.2];
        else
            [UIView setAnimationDuration:0.3];
        [UIView setAnimationCurve:animationCurve];

        CGRect frame = self.footerPadView.frame;
        CGRect keyboardFrame = [self.view convertRect:keyboardEndFrame toView:nil];
        if (up)
            frame.size.height = keyboardFrame.size.height - self.chatView.frame.size.height;
        else
            frame.size.height = 0;
        self.footerPadView.frame = frame;
        self.tableView.tableFooterView = self.footerPadView;
        [UIView commitAnimations];
    }
}
////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)keyboardWillShow:(NSNotification *)aNotification {
    [self moveTextViewForKeyboard:aNotification up:YES];
}
////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)keyboardWillHide:(NSNotification *)aNotification
{
    [self moveTextViewForKeyboard:aNotification up:NO];
}
////////////////////////////////////////////////////////////////////////////////////////////////////
2
daltoniam