Déplacer la vue avec le clavier à l'aide de Swift

278

J'ai une application qui a un champ de texte dans la moitié inférieure de la vue. Cela signifie que lorsque je vais taper dans le champ de texte, le clavier couvre le champ de texte.

Comment pourrais-je déplacer la vue vers le haut lors de la frappe pour pouvoir voir ce que je tape, puis la replacer à sa place d'origine lorsque le clavier disparaît?

J'ai cherché partout mais toutes les solutions semblent être en Obj-C que je ne peux pas encore convertir.

Toute aide serait grandement appréciée.

Alex Catchpole
la source
La meilleure façon de procéder consiste à placer votre contenu dans un UIScrollView , puis à ajuster la propriété contentInset de la vue de défilement en fonction de la hauteur du clavier lorsqu'elle est affichée. N'assumez absolument pas la hauteur du clavier - utilisez la valeur de la notification "le clavier affichera".
nielsbot
1
En fait, les documents Apple vous expliquent comment procéder, sous "Gestion du clavier": developer.apple.com/library/ios/documentation/StringsTextFonts/…
nielsbot
11
Je pense que toutes les réponses ci-dessous ne prennent pas en compte un cas: que faire si vous avez plusieurs champs de texte et que certains d'entre eux sont situés en haut de l'écran? Chaque fois que l'utilisateur it is actually needed to scroll view up when keyboard appears
theDC
Cette réponse est capable de détecter s'il est réellement nécessaire de faire défiler la vue vers le haut lorsque le clavier apparaît en vérifiant si le champ de texte en cours d'édition occupe le même espace que le clavier: stackoverflow.com/a/28813720/6749410
HirdayGupta

Réponses:

710

Voici une solution, sans gérer le passage d'un textField à un autre:

 override func viewDidLoad() {
        super.viewDidLoad()
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)            
    }   

func keyboardWillShow(notification: NSNotification) {            
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
        self.view.frame.origin.y -= keyboardSize.height
    }            
}

func keyboardWillHide(notification: NSNotification) {
    self.view.frame.origin.y = 0
}

Pour résoudre ce problème, remplacez les deux fonctions keyboardWillShow/Hidepar celles-ci:

func keyboardWillShow(notification: NSNotification) {        
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue() {
        if view.frame.origin.y == 0 {
            self.view.frame.origin.y -= keyboardSize.height
        }
    }        
}

func keyboardWillHide(notification: NSNotification) {
    if view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}

MODIFIER POUR SWIFT 3.0:

override func viewDidLoad() {
    super.viewDidLoad()            
    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)    
}

@objc func keyboardWillShow(notification: NSNotification) {        
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= keyboardSize.height
        }
    }        
}

@objc func keyboardWillHide(notification: NSNotification) {
    if self.view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}

MODIFIER POUR SWIFT 4.0:

override func viewDidLoad() {
    super.viewDidLoad()            
    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)    
}

@objc func keyboardWillShow(notification: NSNotification) {        
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= keyboardSize.height
        }
    }        
}

@objc func keyboardWillHide(notification: NSNotification) {
    if self.view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}

MODIFIER POUR SWIFT 4.2:

override func viewDidLoad() {
    super.viewDidLoad()            
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}

@objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= keyboardSize.height
        }
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    if self.view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}
Boris
la source
56
Si l'utilisateur touche un autre champ de texte alors que le clavier est présent, la vue sera poussée plus haut ce qui provoque une zone noire (la taille du clavier) - nous devons corriger cela en ayant une variable qui suit si le clavier est présent ou non . Par exemple, si keyboardPresent == true, ne déplacez pas l'origine de la vue, etc., etc.
jonprasetyo
3
@Matthew Lin utilise un booléen pour que les fonctions keyboardWillShow et hide ne fonctionnent qu'une seule fois
iluvatar_GR
11
Juste une suggestion, pour que vous n'ayez pas à déboguer autant que moi. Si vous avez plusieurs champs uitext sur le même écran, la taille du clavier peut varier (il n'affiche pas de suggestions pour certaines entrées en fonction de vos paramètres), il est donc conseillé de définir self.view.frame.origin.y = 0, à chaque fois vous rejetez le clavier. Par exemple, il afficherait des sugeestions pour votre champ de texte de messagerie, donc la taille du clavier augmenterait, et il n'afficherait pas de suggestions pour le champ de mot de passe, donc la taille du clavier diminuerait.
Vandan Patel
36
Vous devez utiliser UIKeyboardFrameEndUserInfoKeyplutôt que UIKeyboardFrameBeginUserInfoKeypour obtenir la taille du clavier. Je ne sais pas pourquoi pour le moment, mais le premier donnera des résultats plus cohérents.
jshapy8
3
Veuillez remplacer UIKeyboardFrameBeginUserInfoKeypar UIKeyboardFrameEndUserInfoKey. La première donne l'image de début du clavier, qui arrive parfois à zéro, tandis que la seconde donne l'image de fin du clavier.
Arnab
90

Le moyen le plus simple qui ne nécessite même pas de code:

  1. Téléchargez KeyboardLayoutConstraint.swift et ajoutez (faites glisser et déposez) le fichier dans votre projet, si vous n'utilisez pas déjà le framework d'animation Spring.
  2. Dans votre storyboard, créez une contrainte inférieure pour la vue ou le champ de texte, sélectionnez la contrainte (double-cliquez dessus) et dans l'inspecteur d'identité, changez sa classe de NSLayoutConstraint en KeyboardLayoutConstraint.
  3. Terminé!

L'objet se déplacera automatiquement vers le haut avec le clavier, en synchronisation.

gammachill
la source
4
Pour sélectionner la contrainte inférieure, vous pouvez également accéder à l'inspecteur de taille, puis double-cliquer sur la contrainte dans la liste - raywenderlich.com/wp-content/uploads/2015/09/…
gammachill
3
Cela a fonctionné parfaitement pour moi. c'est littéralement un processus en 2 étapes. 1. Ajoutez le KeyboardLayoutConstraint.swift, 2. Dans le storyboard, créez une contrainte inférieure pour la vue ou le champ de texte. REMARQUE: j'ai supprimé mes contraintes et ajouté une seule contrainte au bas de la vue ou du champ de texte et j'ai changé sa classe de NSLayoutConstraint en KeyboardLayoutConstraint. Ensuite, toutes les vues / champs de texte, etc. ci-dessus, je viens de connecter les contraintes de cet élément à l'élément avec un seul KeyboardLayoutConstraint et le résultat était que tous les éléments en vue étaient déplacés vers le HAUT / BAS lorsque le clavier apparaît / disparaît
Brian
4
C'est la meilleure solution, le code fourni ne codant en dur aucune valeur telle que la longueur ou la courbe de l'animation ou la taille du clavier. Il est également facile à comprendre.
miguelSantirso
3
Cela fonctionne pour moi, mais j'obtiens un espace supplémentaire de 50 px entre le haut du clavier et le bas de mon scrollView. Je me demande si cela est dû à la contrainte inférieure de la zone de sécurité que j'utilise. Quelqu'un a rencontré ça?
Clifton Labrum le
1
Ce fut une réponse impressionnante. Design très cool aussi. Une suggestion: si vos vues de texte / champs de texte sont sur des cellules de vue de tableau, vous remarquerez peut-être que les vues qui ont cette contrainte sauteront maladroitement chaque fois que l'utilisateur clique sur Entrée et passe au champ de texte suivant. Vous pouvez envelopper les animations DispatchQueue.main.async {}pour le corriger. Bon travail! Pouces vers le haut!
ScottyBlades
68

L'une des réponses populaires sur ce sujet utilise le code suivant:

func keyboardWillShow(sender: NSNotification) {
    self.view.frame.origin.y -= 150
}
func keyboardWillHide(sender: NSNotification) {
    self.view.frame.origin.y += 150
}

Il y a un problème évident avec la compensation statique de votre vue. Il aura fière allure sur un appareil, mais sera mauvais sur toute autre configuration de taille. Vous devrez obtenir la hauteur des claviers et l'utiliser comme valeur de décalage.

Voici une solution qui fonctionne sur tous les appareils et gère le cas de bord où l'utilisateur masque le champ de texte prédictif lors de la frappe.

Solution

Important à noter ci-dessous, nous passons self.view.window comme paramètre d'objet. Cela nous fournira des données de notre clavier, telles que sa hauteur!

@IBOutlet weak var messageField: UITextField!

override func viewDidLoad() {
    super.viewDidLoad()

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

func keyboardWillHide(sender: NSNotification) {
    let userInfo: [NSObject : AnyObject] = sender.userInfo!
    let keyboardSize: CGSize = userInfo[UIKeyboardFrameBeginUserInfoKey]!.CGRectValue.size
    self.view.frame.origin.y += keyboardSize.height
}

Nous allons le rendre agréable sur tous les appareils et gérer le cas où l'utilisateur ajoute ou supprime le champ de texte prédictif.

func keyboardWillShow(sender: NSNotification) {
    let userInfo: [NSObject : AnyObject] = sender.userInfo!
    let keyboardSize: CGSize = userInfo[UIKeyboardFrameBeginUserInfoKey]!.CGRectValue.size
    let offset: CGSize = userInfo[UIKeyboardFrameEndUserInfoKey]!.CGRectValue.size

    if keyboardSize.height == offset.height {
        UIView.animateWithDuration(0.1, animations: { () -> Void in
            self.view.frame.origin.y -= keyboardSize.height
        })
    } else {
        UIView.animateWithDuration(0.1, animations: { () -> Void in
            self.view.frame.origin.y += keyboardSize.height - offset.height
        })
    }
}

Supprimer des observateurs

N'oubliez pas de retirer vos observateurs avant de quitter la vue pour éviter la transmission de messages inutiles.

override func viewWillDisappear(animated: Bool) {
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: self.view.window)
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: self.view.window)
}

Mise à jour basée sur la question des commentaires:

Si vous avez deux ou plusieurs champs de texte, vous pouvez vérifier si votre view.frame.origin.y est à zéro.

func keyboardWillShow(sender: NSNotification) {
    let userInfo: [NSObject : AnyObject] = sender.userInfo!

    let keyboardSize: CGSize = userInfo[UIKeyboardFrameBeginUserInfoKey]!.CGRectValue.size
    let offset: CGSize = userInfo[UIKeyboardFrameEndUserInfoKey]!.CGRectValue.size

    if keyboardSize.height == offset.height {
        if self.view.frame.origin.y == 0 {
            UIView.animateWithDuration(0.1, animations: { () -> Void in
                self.view.frame.origin.y -= keyboardSize.height
            })
        }
    } else {
        UIView.animateWithDuration(0.1, animations: { () -> Void in
            self.view.frame.origin.y += keyboardSize.height - offset.height
        })
    }
     print(self.view.frame.origin.y)
}
Dan Beaulieu
la source
1
lorsqu'il s'agit de plusieurs champs de texte, la vue continue de monter et ne redescend pas
Mugunthan Balakrishnan
Vous allez devoir changer vos conditions pour tenir compte des champs de texte
Dan Beaulieu
merci pour la réponse, j'ai trouvé la réponse que je cherchais à ce fil sur stack overflow stackoverflow.com/questions/1126726/…
Mugunthan Balakrishnan
@MugunthanBalakrishnan merci d'avoir soulevé cette question, j'ai ajouté une solution.
Dan Beaulieu
Salut les gars, il y a un bug. Les observateurs ne sont pas supprimés de la vue après avoir été appelés dans viewWillDisappear. Remplacez cette ligne "NSNotificationCenter.defaultCenter (). RemoveObserver (self, nom: UIKeyboardWillHideNotification, object: self.view.window)" par "NSNotificationCenter.defaultCenter (). RemoveObserver (self, nom: UIKeyboardWillHideNotification, objet: nil)" puis observer est supprimé
rudald
29

Ajoutez ceci à votre viewcontroller. Fonctionne comme un charme. Ajustez simplement les valeurs.

override func viewDidLoad() {
    super.viewDidLoad()        
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name:NSNotification.Name.UIKeyboardWillShow, object: nil);
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name:NSNotification.Name.UIKeyboardWillHide, object: nil);
}

@objc func keyboardWillShow(sender: NSNotification) {
    self.view.frame.origin.y -= 150
}
@objc func keyboardWillHide(sender: NSNotification) {
    self.view.frame.origin.y += 150
}
user3677173
la source
Cela fonctionne pour moi. Cependant c'est un peu saccadé. Comment puis-je obtenir cela pour remonter en douceur? il existe également un moyen de l'appliquer uniquement à l'un des champs de texte, comme il le fait actuellement pour tous. :(
DannieCoderBoi
2
Il est possible que cela ne fonctionne pas avec "Disposition automatique". Dans ce cas, pensez à le désactiver.
Teodor Ciuraru
1
Cela provoque un comportement génial avec la mise en page automatique @Josh, vous vous trompez
Mike
5
Ne fais pas ça! Vous ne pouvez pas supposer que le clavier a une certaine taille.
nielsbot
2
Devrait utiliser keyboardSize. Que se passe-t-il lorsque vous avez des vues d'accessoires et différentes hauteurs de clavier sur les appareils? Clavier détaché?
xtrinch
25

J'ai un peu amélioré l'une des réponses pour la faire fonctionner avec différents claviers et différents affichages de texte / champs sur une seule page:

Ajouter des observateurs:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(notification:)), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)

    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}

func keyboardWillHide() {
    self.view.frame.origin.y = 0
}

func keyboardWillChange(notification: NSNotification) {

    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        if YOURTEXTVIEW.isFirstResponder {
            self.view.frame.origin.y = -keyboardSize.height
        }
    }
}

Supprimer des observateurs:

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}
scipianne
la source
3
cette solution fonctionne mieux que la réponse acceptée. La réponse acceptée ne montre le clavier qu'une seule fois, ce qui pour moi est un bug :)
Masih
Cela fonctionne pour Xcode 10.1 et iOS 12. La réponse acceptée n'est plus valide.
zeeshan
C'est une excellente réponse, la seule chose que j'ajouterais est de garder une trace de la zone de sécurité inférieure dans les nouveaux appareils (X, XS, etc.) afin que cela prenne en compte cela.
Munib
@Munib Voir stackoverflow.com/a/54993623/1485230 D'autres problèmes incluent la vue non animée et le changement de hauteur du clavier non suivi.
Antzi
que faire si mon textField est en haut de la vue ..? je veux dire s'il y a un champ de texte avec l'origine Y = 0 .. ?? alors textField monte et je ne peux pas le voir
Saifan Nadaf
20

Pas une publicité ou une promotion ou un spam , juste une bonne solution. Je sais que cette question a près de 30 réponses et je suis tellement choqué que personne n'a même mentionné une seule fois ce beau projet GitHub qui fait tout pour vous et encore mieux. Toutes les réponses déplacent simplement la vue vers le haut. Je viens de résoudre tous mes problèmes avec ce IQKeyboardManager. Il a plus de 13000 étoiles.
Ajoutez simplement ceci dans votre podfile si vous utilisez swift

pod 'IQKeyboardManagerSwift'

puis à l'intérieur de votre AppDelegate.swift faire import IQKeyboardManagerSwift

import IQKeyboardManagerSwift

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

      IQKeyboardManager.shared.enable = true // just add this line

      return true
    }
}

Ajouter la ligne IQKeyboardManager.shared.enable = truepour l'activer
Cette solution est indispensable si vous allez en production.

weegee
la source
C'est vraiment bien, mais la dernière version ne fonctionne pas pour moi, j'ai utilisé 6.2.1, et importé comme import IQKeyboardManageret utilisé IQKeyboardManager.shared().isEnabled = truedans AppDelegate
Dhanu K
2
Et cela fonctionne très bien lors de l'utilisation de plusieurs textes d'édition, cela m'a fait gagner du temps
Dhanu K
1
Je ne remercierai jamais assez d'avoir signalé cette merveilleuse bibliothèque. Cette bibliothèque est enfin LA RÉPONSE FINALE à toutes les bêtises liées au clavier pour lesquelles Apple n'a jamais fourni de solution. Maintenant, je vais l'utiliser pour tous mes projets, nouveaux et anciens, et gagner du temps et des maux de tête que ce clavier apparaisse, disparaît ou ne disparaît pas, ou comment le cacher, et pourquoi il se chevauche, des problèmes me causent depuis le jour où je programme pour les iPhones.
zeeshan
1
J'avais écrit mon propre code pour déplacer le champ de texte, un peu comme les autres solutions ici. J'ai trouvé que IQKeyboardManager est très bon et je vais l'intégrer à partir de maintenant. Gardera mon code à portée de main au cas où le cadre ne serait pas mis à jour.
TM Lynch
1
Cette réponse doit être plus élevée, beaucoup plus élevée.
JC Il y a
15

Pour les erreurs d'écran noir (Swift 4 et 4.2) .

J'ai corrigé le problème d'écran noir. Dans la solution vérifiée La hauteur du clavier change après avoir tapé et cela provoque un écran noir.

Vous devez utiliser UIKeyboardFrameEndUserInfoKey au lieu de UIKeyboardFrameBeginUserInfoKey

var isKeyboardAppear = false

override func viewDidLoad() {
    super.viewDidLoad() 
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

@objc func keyboardWillShow(notification: NSNotification) {
    if !isKeyboardAppear {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
            if self.view.frame.origin.y == 0{
                self.view.frame.origin.y -= keyboardSize.height
            }
        }
        isKeyboardAppear = true
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    if isKeyboardAppear {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
            if self.view.frame.origin.y != 0{
                self.view.frame.origin.y += keyboardSize.height
            }
        }
         isKeyboardAppear = false
    }
}
Ali Ihsan URAL
la source
Ne fonctionnera pas s'il y a une barre d'onglets. Vous devez calculer la hauteur de la barre de tabulation, sinon il y aura un espace d'écran noir entre le clavier et la vue.
Ankur Lahiry
Cela ne corrige pas la zone noire où se trouvait le clavier sur l'iPhone X et les versions plus récentes. Et chaque fois que le clavier apparaît et disparaît, la vue principale continue de glisser vers le bas.
Edison
14

Je vois que toutes les réponses déplacent la vue elle-même de la valeur de la hauteur du clavier. Eh bien, j'ai une réponse élaborée, qui pourrait être utile si vous utilisez des contraintes, par exempleautolayout - qui déplace une vue en modifiant sa valeur de contrainte (contraintes inférieure ou supérieure par exemple) par une valeur prédéfinie ou vous pouvez utiliser une valeur de taille de clavier.

Dans cet exemple, j'utilise la contrainte inférieure du champ de texte à la vue de présentation inférieure avec une valeur initiale de 175.

@IBOutlet weak var bottomConstraint: NSLayoutConstraint!

override func viewDidLoad() {
    super.viewDidLoad()        
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name:UIKeyboardWillShowNotification, object: nil);
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name:UIKeyboardWillHideNotification, object: nil);
}

func keyboardWillShow(notification: NSNotification) {
    //To retrieve keyboard size, uncomment following line
    //let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()
    bottomConstraint.constant = 260
    UIView.animateWithDuration(0.3) {
        self.view.layoutIfNeeded()
    }
}

func keyboardWillHide(notification: NSNotification) {
    //To retrieve keyboard size, uncomment following line
    //let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()
    bottomConstraint.constant = 175
    UIView.animateWithDuration(0.3) {
        self.view.layoutIfNeeded()
    }
}
Amro Shafie
la source
Bonjour monsieur, pourriez-vous s'il vous plaît me dire pourquoi cela ne fonctionne pas lorsqu'il est placé dans une vue qui contient également une TableView? Il fonctionne correctement dans le même scénario lorsqu'il contient un CollectionView.
elarcoiris
9

Il y a eu quelques changements dans la façon dont nous définissons la KeyboardWillHideNotification.

Cette solution fonctionne avec Swift 4.2 :

NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)


@objc func keyboardWillShow(_ notification:Notification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
        self.view.frame.origin.y -= keyboardSize.height
    }
}

@objc func keyboardWillHide(_ notification:Notification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        self.view.frame.origin.y += keyboardSize.height
    }
}
Ivan Le Hjelmeland
la source
2
que faire si mon textField est en haut de la vue ..? je veux dire s'il y a un champ de texte avec l'origine Y = 0 .. ?? alors textField monte et je ne peux pas le voir
Saifan Nadaf
7

Swift 5.0:

Après 4-5 heures de combat, je suis venu avec une simple extension de UIViewController avec un code simple qui fonctionne comme un charme

* La vue ne doit pas bouger lorsque TextField est au-dessus du clavier

* Pas besoin de définir une valeur constante sur NSLayoutConstraint

* Aucune bibliothèque tierce partie requise

* Aucun code d'animation requis

* Fonctionne également sur tableview

* Cela fonctionne sur la mise en page automatique / redimensionnement automatique

extension UIViewController {
    func addKeyboardObserver() {
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardNotifications(notification:)),
                                               name: UIResponder.keyboardWillChangeFrameNotification,
                                               object: nil)
    }

    func removeKeyboardObserver(){
        NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillChangeFrameNotification, object: nil)
    }

    // This method will notify when keyboard appears/ dissapears
    @objc func keyboardNotifications(notification: NSNotification) {

        var txtFieldY : CGFloat = 0.0  //Using this we will calculate the selected textFields Y Position
        let spaceBetweenTxtFieldAndKeyboard : CGFloat = 5.0 //Specify the space between textfield and keyboard


        var frame = CGRect(x: 0, y: 0, width: 0, height: 0)
        if let activeTextField = UIResponder.currentFirst() as? UITextField ?? UIResponder.currentFirst() as? UITextView {
            // Here we will get accurate frame of textField which is selected if there are multiple textfields
            frame = self.view.convert(activeTextField.frame, from:activeTextField.superview)
            txtFieldY = frame.origin.y + frame.size.height
        }

        if let userInfo = notification.userInfo {
            // here we will get frame of keyBoard (i.e. x, y, width, height)
            let keyBoardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
            let keyBoardFrameY = keyBoardFrame!.origin.y
            let keyBoardFrameHeight = keyBoardFrame!.size.height

            var viewOriginY: CGFloat = 0.0
            //Check keyboards Y position and according to that move view up and down
            if keyBoardFrameY >= UIScreen.main.bounds.size.height {
                viewOriginY = 0.0
            } else {
                // if textfields y is greater than keyboards y then only move View to up
                if txtFieldY >= keyBoardFrameY {

                    viewOriginY = (txtFieldY - keyBoardFrameY) + spaceBetweenTxtFieldAndKeyboard

                    //This condition is just to check viewOriginY should not be greator than keyboard height
                    // if its more than keyboard height then there will be black space on the top of keyboard.
                    if viewOriginY > keyBoardFrameHeight { viewOriginY = keyBoardFrameHeight }
                }
            }

            //set the Y position of view
            self.view.frame.origin.y = -viewOriginY
        }
    }
}

Ajoutez cette extension de UIResponder pour obtenir quel TextField est sélectionné

extension UIResponder {

    static weak var responder: UIResponder?

    static func currentFirst() -> UIResponder? {
        responder = nil
        UIApplication.shared.sendAction(#selector(trap), to: nil, from: nil, for: nil)
        return responder
    }

    @objc private func trap() {
        UIResponder.responder = self
    }
}

Ensuite, utilisez-le dans votre n'importe quel ViewController

   override func viewWillAppear(_ animated: Bool) {
        self.addKeyboardObserver()
    }

    override func viewWillDisappear(_ animated: Bool) {
        self.removeKeyboardObserver()
    }

  • Enregistrez cette notification dans func viewWillAppear(_ animated: Bool)

  • Désenregistrer cette notification dans func viewWillDisappear(_ animated:Bool)

    Téléchargez la démo ici

Saifan Nadaf
la source
Cela semblait être la meilleure solution, mais il y a quelques bugs. 1, le champ de texte monte mais quand je commence à le taper, il saute un peu plus. 2, En paysage lors de la frappe, le champ de texte saute parfois vers la gauche.
Darren
@Darren j'essaie de comprendre ces bugs mais je n'ai pas trouvé, pouvez-vous s'il vous plaît dire où vous avez ces bugs je veux dire pour quelle version / appareil ... ??
Saifan Nadaf
6

Pour Swift 3, j'ai créé une sous-classe UIViewController car j'avais besoin d'un comportement constant dans tous les contrôleurs de vue.

class SomeClassVC: UIViewController {

  //MARK: - Lifecycle
  override func viewDidLoad() {
    super.viewDidLoad()

    addKeyboardObservers()
  }

  override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)

    removeKeyboardObservers()
  }

  //MARK: - Overrides
  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    view.endEditing(true)
  }

  //MARK: - Help
  func addKeyboardObservers() {
    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
  }

  func removeKeyboardObservers() {
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: self.view.window)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: self.view.window)
  }

  func keyboardWillShow(notification: NSNotification) {
    let keyboardHeight = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.height
    UIView.animate(withDuration: 0.1, animations: { () -> Void in
      self.view.window?.frame.origin.y = -1 * keyboardHeight!
      self.view.layoutIfNeeded()
    })
  }

  func keyboardWillHide(notification: NSNotification) {
    UIView.animate(withDuration: 0.1, animations: { () -> Void in
      self.view.window?.frame.origin.y = 0
      self.view.layoutIfNeeded()
    })
  }

  func resignTextFieldFirstResponders() {
    for textField in self.view.subviews where textField is UITextField {
      textField.resignFirstResponder()
    }
  }

  func resignAllFirstResponders() {
      view.endEditing(true)
  }
}
Pavle Mijatovic
la source
Inspiré par la solution de Pavle, je l'ai mis à niveau pour augmenter automatiquement le clavier d'un certain pourcentage de l'espace disponible restant et également trouver le champ focalisé de manière récursive pour une disposition correcte. Prenez-le ici: gist.github.com/noordawod/24d32b2ce8363627ea73d7e5991009a0
Noor Dawod
Ma barre d'onglets se déplace également vers le haut avec la fenêtre! :(
Alfi
6

La réponse validée ne prend pas en compte la position du champ de texte et a un bug (double déplacement, ne jamais revenir à la position principale, déplacement même si le texfield est au dessus de la vue ...)

L'idée est:

  • pour obtenir la position absolue absolue de TextField
  • pour obtenir la hauteur du clavier
  • pour obtenir le ScreenHeight
  • Calculez ensuite la distance entre la position du clavier et le champ de texte (si <0 -> montez la vue)
  • d'utiliser UIView.transform au lieu de UIView.frame.origin.y - = .., car il est plus facile de revenir à sa position d'origine avec UIView.transform = .identity

alors nous ne pourrons déplacer la vue que si nécessaire et du déplacement spécifique afin d'avoir le texField focalisé juste au dessus du clavier

Voici le code:

Swift 4

class ViewController: UIViewController, UITextFieldDelegate {

var textFieldRealYPosition: CGFloat = 0.0

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(VehiculeViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(VehiculeViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

  // Delegate all textfields

}


@objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        let distanceBetweenTextfielAndKeyboard = self.view.frame.height - textFieldRealYPosition - keyboardSize.height
        if distanceBetweenTextfielAndKeyboard < 0 {
            UIView.animate(withDuration: 0.4) {
                self.view.transform = CGAffineTransform(translationX: 0.0, y: distanceBetweenTextfielAndKeyboard)
            }
        }
    }
}


@objc func keyboardWillHide(notification: NSNotification) {
    UIView.animate(withDuration: 0.4) {
        self.view.transform = .identity
    }
}


func textFieldDidBeginEditing(_ textField: UITextField) {
  textFieldRealYPosition = textField.frame.origin.y + textField.frame.height
  //take in account all superviews from textfield and potential contentOffset if you are using tableview to calculate the real position
}

}

Quentin N
la source
Très bien! (Dans viewDidLoad, vous avez "VehiculeViewController" au lieu de simplement "ViewController").
rene
Une réponse beaucoup plus complète et utile. Je vous remercie! Je suggère que la vérification du clavier soit appelée comme suit car elle donnera une taille cohérente du clavier ......... si laissez keyboardSize = (notification.userInfo? [UIResponder.keyboardFrameEndUserInfoKey] as? NSValue) ?. cgRectValue .size
Ben
4

J'ai remarqué que les autres réponses impliquaient de couper une partie du haut de la vue. Si vous souhaitez simplement redimensionner la vue sans couper de contenu, essayez simplement cette méthode :)

func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.view.setTranslatesAutoresizingMaskIntoConstraints(true)
        self.view.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.height - keyboardSize.height)
    }
}

func keyboardWillHide(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.collectionView.setTranslatesAutoresizingMaskIntoConstraints(false)
        self.view.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.height + keyboardSize.height)
    }
}
Grant Park
la source
4

Mes deux cents pour les débutants: dans les exemples ci-dessus, quelqu'un change les coordonnées, d'autres utilisent le "masque de redimensionnement automatique" et d'autres contraintes:

Comme le dit Apple, ne mélangez pas ces 3 types de logique. Si vous avez des contraintes dans Storyboard, n'essayez pas de changer x / y. Cela ne fonctionne définitivement pas.

ingconti
la source
4

Donc, aucune des autres réponses ne semble bien faire les choses.

Le bon clavier sur iOS devrait:

  • Redimensionner automatiquement lorsque le clavier change de taille (OUI IL PEUT)
  • Animez à la même vitesse que le clavier
  • Animer en utilisant la même courbe que le clavier
  • Respectez les zones de sécurité, le cas échéant.
  • Fonctionne également sur iPad / mode non ancré

Mon code utilise un NSLayoutConstraintdéclaré comme@IBOutlet

@IBOutlet private var bottomLayoutConstraint: NSLayoutConstraint!

Vous pouvez également utiliser des transformations, afficher des décalages, .... Je pense que c'est plus facile avec la contrainte tho. Il fonctionne en définissant une contrainte en bas, vous devrez peut-être modifier le code si votre constante n'est pas 0 / Pas en bas.

Voici le code:

// In ViewDidLoad
    NotificationCenter.default.addObserver(self, selector: #selector(?MyViewController.keyboardDidChange), name: UIResponder.keyboardWillChangeFrameNotification, object: nil)


@objc func keyboardDidChange(notification: Notification) {
    let userInfo = notification.userInfo! as [AnyHashable: Any]
    let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
    let animationDuration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber
    let animationCurve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as! NSNumber
    bottomLayoutConstraint.constant = view.frame.height - endFrame.origin.y - view.safeAreaInsets.bottom // If your constraint is not defined as a safeArea constraint you might want to skip the last part.
    // Prevents iPad undocked keyboard.
    guard endFrame.height != 0, view.frame.height == endFrame.height + endFrame.origin.y else {
        bottomLayoutConstraint.constant = 0
        return
    }
    UIView.setAnimationCurve(UIView.AnimationCurve(rawValue: animationCurve.intValue)!)
    UIView.animate(withDuration: animationDuration.doubleValue) {
        self.view.layoutIfNeeded()
        // Do additional tasks such as scrolling in a UICollectionView
    }
}
Antzi
la source
3

sa réponse parfaite à 100% pour toutes les mises à jour de Guy Tableview Height lorsque le clavier est ouvert

Pour Swift4.2

   override func viewDidLoad() {
      super.viewDidLoad()
      NotificationCenter.default.addObserver(self, selector: #selector(RecipeVC.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)

      NotificationCenter.default.addObserver(self, selector: #selector(RecipeVC.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
   }

   @objc func keyboardWillShow(notification: NSNotification) {
    if ((notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue) != nil {

        var userInfo = notification.userInfo!
        var keyboardFrame:CGRect = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        keyboardFrame = self.view.convert(keyboardFrame, from: nil)

        var contentInset:UIEdgeInsets = self.tbl.contentInset
          contentInset.bottom = keyboardFrame.size.height
          self.tbl.contentInset = contentInset
    }
}

   @objc func keyboardWillHide(notification: NSNotification) {
    if ((notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue) != nil {
        let contentInset:UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        self.tbl.contentInset = contentInset
    }
}

Swift3.2

    override func viewDidLoad() {
          super.viewDidLoad()

           NotificationCenter.default.addObserver(self, selector: #selector(RecipeVC.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)

           NotificationCenter.default.addObserver(self, selector: #selector(RecipeVC.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
    }
    func keyboardWillShow(notification: NSNotification) {
         if ((notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue) != nil {
         //self.view.frame.origin.y -= keyboardSize.height
         var userInfo = notification.userInfo!
         var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
          keyboardFrame = self.view.convert(keyboardFrame, from: nil)

          var contentInset:UIEdgeInsets = self.tbl.contentInset
          contentInset.bottom = keyboardFrame.size.height
          self.tbl.contentInset = contentInset

       }
    }

    func keyboardWillHide(notification: NSNotification) {
         if ((notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue) != nil {
         let contentInset:UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
         self.tbl.contentInset = contentInset
         }
    }
Maulik Patel
la source
C'est la meilleure réponse. tbl devrait être tableView et j'ai ajouté du rembourrage: contentInset.bottom = keyboardFrame.size.height + 10
iphaaw
2

Pour Swift 3

func textFieldDidBeginEditing(_ textField: UITextField) { // became first responder

    //move textfields up
    let myScreenRect: CGRect = UIScreen.main.bounds
    let keyboardHeight : CGFloat = 216

    UIView.beginAnimations( "animateView", context: nil)
    var movementDuration:TimeInterval = 0.35
    var needToMove: CGFloat = 0

    var frame : CGRect = self.view.frame
    if (textField.frame.origin.y + textField.frame.size.height + UIApplication.shared.statusBarFrame.size.height > (myScreenRect.size.height - keyboardHeight - 30)) {
        needToMove = (textField.frame.origin.y + textField.frame.size.height + UIApplication.shared.statusBarFrame.size.height) - (myScreenRect.size.height - keyboardHeight - 30);
    }

    frame.origin.y = -needToMove
    self.view.frame = frame
    UIView.commitAnimations()
}

func textFieldDidEndEditing(_ textField: UITextField) {
    //move textfields back down
    UIView.beginAnimations( "animateView", context: nil)
    var movementDuration:TimeInterval = 0.35
    var frame : CGRect = self.view.frame
    frame.origin.y = 0
    self.view.frame = frame
    UIView.commitAnimations()
}
Celil Bozkurt
la source
2

Swift 4:

J'avais un problème avec la réponse la plus acceptée, dans laquelle masquer le clavier ne renvoyait pas la vue jusqu'en bas de la page (seulement partiellement). Cela a fonctionné pour moi (+ mis à jour pour Swift 4).

override func viewDidLoad() {
    super.viewDidLoad()
    self.hideKeyboardWhenTappedAround()
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

@objc func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0{
            self.view.frame.origin.y -= keyboardSize.height
        }
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y != 0{
            self.view.frame.origin.y = 0
        }
    }
}
Rbar
la source
que faire si mon textField est en haut de la vue ..? je veux dire s'il y a un champ de texte avec l'origine Y = 0 .. ?? alors textField monte et je ne peux pas le voir
Saifan Nadaf
2

Semblable à la réponse @Boris, mais dans Swift 5 :

override func viewDidLoad() {
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
}

@IBAction func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= keyboardSize.height
        }
    }
}

@IBAction func keyboardWillHide(notification: NSNotification) {
    if self.view.frame.origin.y != 0 {
        self.view.frame.origin.y = 0
    }
}
Jerry Chong
la source
1

Voici ma solution (en fait, ce code est pour le cas où vous avez peu de champs de texte dans votre vue, cela fonctionne également pour le cas où vous avez un champ de texte)

class MyViewController: UIViewController, UITextFieldDelegate {

@IBOutlet weak var firstTextField: UITextField!
@IBOutlet weak var secondTextField: UITextField!

var activeTextField: UITextField!
var viewWasMoved: Bool = false


override func viewDidLoad() {
    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PrintViewController.keyboardWillShow(_:)), name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(PrintViewController.keyboardWillHide(_:)), name: UIKeyboardWillHideNotification, object: nil)
}

override func viewDidDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

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

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

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


func keyboardWillShow(notification: NSNotification) {

    let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()

    var aRect: CGRect = self.view.frame
    aRect.size.height -= keyboardSize!.height

    let activeTextFieldRect: CGRect? = activeTextField?.frame
    let activeTextFieldOrigin: CGPoint? = activeTextFieldRect?.origin

    if (!CGRectContainsPoint(aRect, activeTextFieldOrigin!)) {
        self.viewWasMoved = true
        self.view.frame.origin.y -= keyboardSize!.height
    } else {
        self.viewWasMoved = false
    }
}

func keyboardWillHide(notification: NSNotification) {
    if (self.viewWasMoved) {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            self.view.frame.origin.y += keyboardSize.height
        }
    }
}
Vah.Sah
la source
N'oubliez pas de définir le délégué sur textFields
SwingerDinger
Modifiez la condition dans le clavier comme si (! CGRectContainsPoint (aRect, newOrgin!) &&! Self.viewWasMoved)
KingofBliss
ajouter self.viewWasMoved = false lors de la réinitialisation du cadre
KingofBliss
1

Mis à jour pour Swift 3 ...

Comme d'autres l'ont dit, vous devez ajouter des observateurs de notification dans la méthode viewDidLoad () de votre contrôleur, comme ceci:

NotificationCenter.default.addObserver(forName: .UIKeyboardWillShow, object: nil, queue: nil)
    { notification in
    self.keyboardWillShow(notification)
    }

NotificationCenter.default.addObserver(forName: .UIKeyboardWillHide, object: nil, queue: nil)
    { notification in
    self.keyboardWillHide(notification)
    }

NotificationCenter.default.addObserver(forName: .UIKeyboardDidShow, object: nil, queue: nil)
    { _ in
    self.enableUserInteraction()
    }

NotificationCenter.default.addObserver(forName: .UIKeyboardDidHide, object: nil, queue: nil)
    { _ in
    self.enableUserInteraction()
    }

N'oubliez pas de supprimer vos observateurs le cas échéant (je le fais dans la méthode viewWillDisappear ())

NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardDidShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardDidHide, object: nil)

Ensuite, implémentez vos méthodes show et hide - notez la ligne qui indique à l'application d'ignorer les événements d'interaction (beginIgnoringInteractionEvents). Ceci est important car sans lui, l'utilisateur pourrait appuyer sur un champ ou même une vue de défilement et provoquer le décalage une deuxième fois, ce qui entraînerait un terrible problème d'interface utilisateur. Ignorer les événements d'interaction avant l'affichage et le masquage du clavier évitera ceci:

func keyboardWillShow(notification: Notification)
    {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
        {
        UIApplication.shared.beginIgnoringInteractionEvents()
        self.view.frame.origin.y -= keyboardSize.height
        // add this line if you are shifting a scrollView, as in a chat application
        self.timelineCollectionView.contentInset.top += keyboardSize.height
        }
    }

func keyboardWillHide(notification: Notification)
    {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue
        {
        UIApplication.shared.beginIgnoringInteractionEvents()
        self.view.frame.origin.y += keyboardSize.height
        // add this line if you are shifting a scrollView, as in a chat application
        self.timelineCollectionView.contentInset.top -= keyboardSize.height
        }
    }

Enfin, réactivez les interactions utilisateur (rappelez-vous, cette méthode se déclenche après le clavier didShow ou didHide):

func enableUserInteraction()
    {
    UIApplication.shared.endIgnoringInteractionEvents()
    }
Gene Loparco
la source
1

Code Swift 3

var activeField: UITextField?

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(ProfileViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(ProfileViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

func textFieldDidBeginEditing(_ textField: UITextField){
    activeField = textField
}

func textFieldDidEndEditing(_ textField: UITextField){
    activeField = nil
}

func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if (self.activeField?.frame.origin.y)! >= keyboardSize.height {
            self.view.frame.origin.y = keyboardSize.height - (self.activeField?.frame.origin.y)!
        } else {
            self.view.frame.origin.y = 0
        }
    }
}

func keyboardWillHide(notification: NSNotification) {
    self.view.frame.origin.y = 0
}
Rohit Sharma
la source
1

Si vous avez 2 champs de texte ou plus sur le même VC, et que l'utilisateur tape sur l'un d'eux puis sur l'autre, sans appeler la fonction keyboardWillHide, la vue monte une fois de plus, ce qui n'est pas nécessaire, car vous aurez le clavier, un espace vide qui a la hauteur du clavier, puis la vue, en utilisant le code dans la réponse que j'ai modifiée:

override func viewDidLoad() {       
    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillShow:"), name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide:"), name: UIKeyboardWillHideNotification, object: nil)
}

func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.view.frame.origin.y -= keyboardSize.height
    }
}

func keyboardWillHide(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.view.frame.origin.y += keyboardSize.height
    }
}

Pour résoudre ce problème, remplacez les deux fonctions "KeyboardWillShow / Hide" par celles-ci:

func keyboardWillShow(notification: NSNotification) {
 if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
    if view.frame.origin.y == 0{
        self.view.frame.origin.y -= keyboardSize.height
        }
    }
}

func keyboardWillHide(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        if view.frame.origin.y != 0 {
            self.view.frame.origin.y += keyboardSize.height
        }
    }
}
M. Xcoder
la source
que faire si mon textField est en haut de la vue ..? je veux dire s'il y a un champ de texte avec l'origine Y = 0 .. ?? alors textField monte et je ne peux pas le voir
Saifan Nadaf
1

@ La solution de Boris est TRÈS bonne mais la vue peut parfois être corrompue.

Pour un alignement parfait, utilisez le code ci-dessous

override func viewDidLoad() {
super.viewDidLoad()            
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)}

Les fonctions:

@objc func keyboardWillShow(notification: NSNotification) {        
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
    if self.view.frame.origin.y == 0{
        self.view.frame.origin.y -= keyboardSize.height
    }
}}    

Et,

@objc func keyboardWillHide(notification: NSNotification) {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
    if self.view.frame.origin.y != 0{
        self.view.frame.origin.y = 0 
    }
} }
Eray Alparslan
la source
0

ce tutoriel vidéo est le meilleur. 7 minutes et ça aura tellement de sens. Une telle solution simple lorsque vous avez plusieurs champs de texte et que vous souhaitez que la vue de défilement déplace la quantité "x" de pixels lorsque ce champ de texte spécifique est appuyé.

https://youtu.be/VuiPGJOEBH4

Juste ces étapes:

-Placez tous vos champs de texte dans une vue de défilement limitée aux bords de la vue.

-Connectez tous les champs de texte et faites défiler la vue en tant que délégués au contrôleur de vue.

-Connectez tous les champs de texte et faites défiler la vue avec un IBOutlet.

class ViewController: UIViewController, UITextFieldDelegate {

-Ajouter le protocole UITextFieldDelegate à votre classe

@IBOutlet var stateAddress: UITextField!
@IBOutlet var zipAddress: UITextField!
@IBOutlet var phoneNumber: UITextField!
@IBOutlet var vetEmailAddress: UITextField!    
@IBOutlet weak var scrollView: UIScrollView!

-Ajoutez les méthodes UITextFieldDelegate à votre fichier swift:

func textFieldShouldReturn(textField: UITextField) -> Bool {

    textField.resignFirstResponder()
    return true
}


func textFieldDidBeginEditing(textField: UITextField) {

    if (textField == self.stateAddress) {
        scrollView.setContentOffset(CGPointMake(0, 25), animated: true)
    }
    else if (textField == self.zipAddress) {
        scrollView.setContentOffset(CGPointMake(0, 57), animated: true)
    }
    else if (textField == self.phoneNumber) {
        scrollView.setContentOffset(CGPointMake(0, 112), animated: true)
    }
    else if (textField == self.vetEmailAddress) {
        scrollView.setContentOffset(CGPointMake(0, 142), animated: true)
    }
}

func textFieldDidEndEditing(textField: UITextField) {

    scrollView.setContentOffset(CGPointMake(0, 0), animated: true)
}

La première méthode active simplement le bouton de retour sur le clavier pour fermer le clavier. La seconde est lorsque vous appuyez sur n'importe quel champ de texte spécifique, puis définissez le décalage y de la distance de défilement de votre vue (la mienne est basée sur l'emplacement y sur mes contrôleurs de vue 25, 57, 112, 142). Le dernier dit que lorsque vous appuyez loin du clavier, la vue de défilement revient à son emplacement d'origine.

J'ai rendu ma vue pixel parfaite de cette façon!

bme003
la source
0

Cette fonctionnalité devrait être intégrée à Ios, mais nous devons la faire en externe.
Insérez le code ci-dessous
* Pour déplacer la vue lorsque textField est sous le clavier,
* Ne pas déplacer la vue lorsque textField est au-dessus du clavier
* Pour déplacer la vue en fonction de la hauteur du clavier si nécessaire.
Cela fonctionne et testé dans tous les cas.

import UIKit

class NamVcc: UIViewController, UITextFieldDelegate
{
    @IBOutlet weak var NamTxtBoxVid: UITextField!

    var VydTxtBoxVar: UITextField!
    var ChkKeyPadDspVar: Bool = false
    var KeyPadHytVal: CGFloat!

    override func viewDidLoad()
    {
        super.viewDidLoad()

        NamTxtBoxVid.delegate = self
    }

    override func viewWillAppear(animated: Bool)
    {
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: #selector(TdoWenKeyPadVyd(_:)),
            name:UIKeyboardWillShowNotification,
            object: nil);
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: #selector(TdoWenKeyPadHyd(_:)),
            name:UIKeyboardWillHideNotification,
            object: nil);
    }

    func textFieldDidBeginEditing(TxtBoxPsgVar: UITextField)
    {
        self.VydTxtBoxVar = TxtBoxPsgVar
    }

    func textFieldDidEndEditing(TxtBoxPsgVar: UITextField)
    {
        self.VydTxtBoxVar = nil
    }

    func textFieldShouldReturn(TxtBoxPsgVar: UITextField) -> Bool
    {
        self.VydTxtBoxVar.resignFirstResponder()
        return true
    }

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
    {
        view.endEditing(true)
        super.touchesBegan(touches, withEvent: event)
    }

    func TdoWenKeyPadVyd(NfnPsgVar: NSNotification)
    {
        if(!self.ChkKeyPadDspVar)
        {
            self.KeyPadHytVal = (NfnPsgVar.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().height

            var NonKeyPadAraVar: CGRect = self.view.frame
            NonKeyPadAraVar.size.height -= self.KeyPadHytVal

            let VydTxtBoxCenVal: CGPoint? = VydTxtBoxVar?.frame.origin

            if (!CGRectContainsPoint(NonKeyPadAraVar, VydTxtBoxCenVal!))
            {
                self.ChkKeyPadDspVar = true
                UIView.animateWithDuration(1.0,
                    animations:
                    { self.view.frame.origin.y -= (self.KeyPadHytVal)},
                    completion: nil)
            }
            else
            {
                self.ChkKeyPadDspVar = false
            }
        }

    }

    func TdoWenKeyPadHyd(NfnPsgVar: NSNotification)
    {
        if (self.ChkKeyPadDspVar)
        {
            self.ChkKeyPadDspVar = false
            UIView.animateWithDuration(1.0,
                animations:
                { self.view.frame.origin.y += (self.KeyPadHytVal)},
                completion: nil)
        }
    }

    override func viewDidDisappear(animated: Bool)
    {
        super.viewWillDisappear(animated)
        NSNotificationCenter.defaultCenter().removeObserver(self)
        view.endEditing(true)
        ChkKeyPadDspVar = false
    }
}

| :: | Parfois, la vue sera vers le bas, dans ce cas, utilisez la hauteur +/- 150:

    NonKeyPadAraVar.size.height -= self.KeyPadHytVal + 150

    { self.view.frame.origin.y -= self.KeyPadHytVal  - 150},
                    completion: nil)

    { self.view.frame.origin.y += self.KeyPadHytVal  - 150},
                completion: nil)
Sujay UN
la source
0
func keyboardWillShow(notification: NSNotification) {

    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.view.frame.origin.y = self.view.frame.height - (self.view.frame.height + keyboardSize.height)
    }

}

func keyboardWillHide(notification: NSNotification) {
        self.view.frame.origin.y = 0
}

il doit être plus stable

Hadevs Play
la source
0
 override func viewWillAppear(animated: Bool)
 {
 super.viewWillAppear(animated)

 NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillShow:", name: UIKeyboardWillShowNotification, object: nil)
 NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillHide:", name: UIKeyboardWillHideNotification, object: nil)

 }

 // MARK: - keyboard
 func keyboardWillShow(notification: NSNotification) 
{

if let userInfo = notification.userInfo {
if let keyboardSize = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
let contentInsets = self.tblView.contentInset as UIEdgeInsets
self.tblView.contentInset = UIEdgeInsets(top: contentInsets.top, left: contentInsets.left, bottom: keyboardSize.height, right:contentInsets.right)
                    // ...
                } else {
                    // no UIKeyboardFrameBeginUserInfoKey entry in userInfo
                }
            } else {
                // no userInfo dictionary in notification
            }
        }

func keyboardWillHide(notification: NSNotification) 
{
let contentInsets = self.tblView.contentInset as UIEdgeInsets
self.tblView.contentInset = UIEdgeInsets(top: contentInsets.top, left: contentInsets.left, bottom: 0, right:contentInsets.right)
 }
Himali Shah
la source
0

Utilisez le code suivant pour afficher Up sur UITextField Clicked

func textFieldDidBeginEditing(textField: UITextField) {
    ViewUpanimateMoving(true, upValue: 100)
}
func textFieldDidEndEditing(textField: UITextField) {
    ViewUpanimateMoving(false, upValue: 100)
}
func ViewUpanimateMoving (up:Bool, upValue :CGFloat){
    var durationMovement:NSTimeInterval = 0.3
    var movement:CGFloat = ( up ? -upValue : upValue)
    UIView.beginAnimations( "animateView", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(durationMovement)
    self.view.frame = CGRectOffset(self.view.frame, 0,  movement)
    UIView.commitAnimations()
}
Jugal K Balara
la source
0

J'ai fait un cocoapod pour simplifier la chose:

https://github.com/xtrinch/KeyboardLayoutHelper

Comment l'utiliser:

Faites une contrainte de fond de disposition automatique, donnez-lui une classe de KeyboardLayoutConstraint dans le module KeyboardLayoutHelper et le pod fera le travail nécessaire pour l'augmenter pour accueillir le clavier qui apparaît et disparaît. Voir l'exemple de projet sur les exemples d'utilisation (j'en ai créé deux: textFields à l'intérieur d'un scrollView et textFields centrés verticalement avec deux vues de base - connexion et enregistrement).

La contrainte de mise en page inférieure peut être de la vue conteneur, du champ textField lui-même, n'importe quoi, vous le nommez.

xtrinch
la source