Sposta il campo di testo quando la tastiera appare rapida


217

Sto usando Swift per programmare con iOS e sto usando questo codice per spostare il UITextField, ma non funziona. Chiamo la funzione keyboardWillShowcorrettamente, ma il campo di testo non si sposta. Sto usando il layout automatico.

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

deinit {
    NSNotificationCenter.defaultCenter().removeObserver(self);
}

func keyboardWillShow(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        //let contentInsets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardSize.height, right: 0)

        var frame = self.ChatField.frame
        frame.origin.y = frame.origin.y - keyboardSize.height + 167
        self.chatField.frame = frame
        println("asdasd")
    }
}

2
Guida dettagliata con file di progetto: codebeaulieu.com/43/…
Dan Beaulieu

Forse deinit e viewDidLoad non sono bilanciati.
Ricardo,

Basato sui documenti di Apple e sull'esperienza personale. Ecco il mio repository git usando UIScrollView per spostare TF: github.com/29satnam/MoveTextFieldWhenKeyboardAppearsSwift
Codetard

Risposte:


317

Ci sono un paio di miglioramenti da apportare alle risposte esistenti.

Innanzitutto UIKeyboardWillChangeFrameNotification è probabilmente la migliore notifica in quanto gestisce i cambiamenti che non sono solo mostra / nascondi ma anche cambiamenti dovuti alla modifica della tastiera (lingua, utilizzo di tastiere di terze parti ecc.) E anche alle rotazioni (ma il commento della nota che indica che la tastiera si nasconderà dovrebbe anche essere gestito per supportare la connessione della tastiera hardware).

In secondo luogo, i parametri di animazione possono essere estratti dalla notifica per garantire che le animazioni siano correttamente unite.

Probabilmente ci sono opzioni per ripulire un po 'di più questo codice, specialmente se ti senti a tuo agio nel disfare il codice del dizionario.

Swift 3

class MyViewController: UIViewController {

// This constraint ties an element at zero points from the bottom layout guide
@IBOutlet var keyboardHeightLayoutConstraint: NSLayoutConstraint?

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

deinit {
    NotificationCenter.default.removeObserver(self)
}

@objc func keyboardNotification(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
        let endFrameY = endFrame.origin.y ?? 0
        let duration:TimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
        let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
        let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIViewAnimationOptions.curveEaseInOut.rawValue
        let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
        if endFrameY >= UIScreen.main.bounds.size.height {
            self.keyboardHeightLayoutConstraint?.constant = 0.0
        } else {
            self.keyboardHeightLayoutConstraint?.constant = endFrame?.size.height ?? 0.0
        }
        UIView.animate(withDuration: duration,
                                   delay: TimeInterval(0),
                                   options: animationCurve,
                                   animations: { self.view.layoutIfNeeded() },
                                   completion: nil)
    }
}

(Modificato per tenere conto delle animazioni della tastiera fuori dallo schermo invece di ridurle, come nel fantastico commento di @ Gabox di seguito)

Swift 5

class MyViewController: UIViewController {

// This constraint ties an element at zero points from the bottom layout guide
@IBOutlet var keyboardHeightLayoutConstraint: NSLayoutConstraint?

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self,
        selector: #selector(self.keyboardNotification(notification:)),
        name: UIResponder.keyboardWillChangeFrameNotification,
        object: nil)
}

deinit {
    NotificationCenter.default.removeObserver(self)
}

@objc func keyboardNotification(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        let endFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
        let endFrameY = endFrame?.origin.y ?? 0
        let duration:TimeInterval = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
        let animationCurveRawNSN = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
        let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
        let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
        if endFrameY >= UIScreen.main.bounds.size.height {
            self.keyboardHeightLayoutConstraint?.constant = 0.0
        } else {
            self.keyboardHeightLayoutConstraint?.constant = endFrame?.size.height ?? 0.0
        }
        UIView.animate(withDuration: duration,
                                   delay: TimeInterval(0),
                                   options: animationCurve,
                                   animations: { self.view.layoutIfNeeded() },
                                   completion: nil)
    }
}

1
@JosephLord nice. ma ho scoperto che non funziona quando la tastiera si nasconde perché endFrame?.size.heightnon è nulla. Ho ottenuto il frame finale come UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 1024}, {768, 264}}";. Ho funzionato su iOS 8.3 iPad Simulator, Portrait. Xcode6.3 beta4.
Hlung,

8
se la tastiera non si nasconde, prova a utilizzare questo codice se endFrame? .origin.y> = UIScreen.mainScreen (). bounds.size.height {self.keyboardHeightLayoutConstraint? .constant = 0.0} else {self.keyboardHeightLayoutConstraint? .constant = endFrame.size.height}
Gabriel Goncalves,

3
keyBoardHeightLayoutConstraint è un vincolo definito in InterfaceBuilder che limita la parte inferiore della vista che si desidera spostare / ridurre alla guida di layout inferiore o alla parte inferiore della vista principale per il viewcontroller. La costante è inizialmente impostata su zero e verrà regolata per fare spazio alla tastiera quando la tastiera appare o cambia dimensione.
Joseph Lord,

2
Nota che .UIKeyboardWillChangeFramenon si attiva quando è collegata una tastiera hardware, anche se la tastiera iOS scompare. È necessario osservare .UIKeyboardWillHideanche per catturare quel caso limite.
jamesk,

1
@Sulthan funziona bene .. Il mio problema è che sta diventando un po 'più alto rispetto al keybaord. esiste un modo per risolvere questo problema?
Pavlos,

128

Se stai utilizzando il layout automatico, suppongo che tu abbia impostato lo spazio inferiore su Vincolo supervisione . In tal caso, devi semplicemente aggiornare il valore del vincolo. Ecco come lo fai con un po 'di animazione.

func keyboardWasShown(notification: NSNotification) {
    let info = notification.userInfo!
    let keyboardFrame: CGRect = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue()

    UIView.animateWithDuration(0.1, animations: { () -> Void in
        self.bottomConstraint.constant = keyboardFrame.size.height + 20
    })
}

Il 20 hardcoded viene aggiunto solo per far apparire il campo di testo sopra la tastiera solo un po '. Altrimenti il ​​margine superiore della tastiera e il margine inferiore del campo di testo si toccherebbero.

Quando la tastiera viene ignorata, reimpostare il valore del vincolo su quello originale.


1
Puoi spiegarmi come lo definisco? Grazie! controllo sempre tutto sullo storyboard
Pedro Manfredi,

4
bottomConstraint è il nome che ho dato al vincolo. Ho selezionato il costruttore, trascinato e creato un IBOutlet e ho dato quel nome. È possibile creare IBOutlet ai vincoli proprio come si farebbe con altri elementi dell'interfaccia utente come pulsanti e campi di testo.
Isuru,

2
Questa risposta ha funzionato benissimo tranne che l'animazione è avvenuta immediatamente per me. Verifica Come posso animare le modifiche ai vincoli? per come animare correttamente.
Adam Johns,

2
@ vinbhai4u Devi registrarti per la UIKeyboardWillShowNotificationnotifica. Guarda il codice nella domanda del PO.
Isuru,

8
@AdamJohns Per animare la modifica del vincolo, aggiorna la costante all'esterno di animateWithDuratione chiama self.view.layoutIfNeeded()all'interno del blocco animato.
Max

111

Una soluzione semplice è spostare la vista verso l'alto con costante dell'altezza della tastiera.

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

@objc func keyboardWillShow(sender: NSNotification) {
     self.view.frame.origin.y = -150 // Move view 150 points upward 
}

@objc func keyboardWillHide(sender: NSNotification) {
     self.view.frame.origin.y = 0 // Move view to original position  
}

Swift 5:

NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(sender:)), name: UIResponder.keyboardWillShowNotification, object: nil);

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

4
Mi piace questa semplice soluzione. Ma ho aggiunto una tastiera che mostra un valore booleano da quando ho spostato più di un campo di testo. Sposto la tastiera solo una volta mentre viene mostrato. Grazie.
Ken,

2
invece di spostare la vista, sposta textView
ericgu

1
Ciò manterrà meno il valore y della vista se l'utente cambia la lingua di input.
Jeffrey Neo,

invece di cambiare il self.view ho fatto il valore self.myConstraint, funziona ma il punto è che la vista (a cui viene applicato il vincolo) continua a salire. qualcuno ha affrontato questo problema?
Sashi,

5
Invece di usare self.view.frame.origin.y -= 150use self.view.frame.origin.y = -150e invece di self.view.frame.origin.y += 150use self.view.frame.origin.y = 0. Ciò impedisce alla vista di spostarsi di 150 ogni volta che viene toccato un nuovo campo.
Gunwin

44

Per spostare la vista durante la modifica del campo di testo, prova questo, l'ho applicato,

Opzione 1: - ** ** Aggiornamento in Swift 5.0 e iPhone X, XR, XS e XS Max Move utilizzando NotificationCenter

  • Registrare questa notifica in func viewWillAppear(_ animated: Bool)

  • Annullare la registrazione in func viewWillDisappear(_ animated: Bool)

Nota: - Se non si annulla la registrazione di quanto chiamerà dalla classe figlio e si causerà il crash o altro.

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillShow(notification:)), name:  UIResponder.keyboardWillShowNotification, object: nil )
}
override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
}

@objc func keyboardWillShow( notification: Notification) {
    if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
        var newHeight: CGFloat
        let duration:TimeInterval = (notification.userInfo![UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
        let animationCurveRawNSN = notification.userInfo![UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
        let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
        let animationCurve:UIView.AnimationOptions = UIView.AnimationOptions(rawValue: animationCurveRaw)
        if #available(iOS 11.0, *) {
            newHeight = keyboardFrame.cgRectValue.height - self.view.safeAreaInsets.bottom
        } else {
            newHeight = keyboardFrame.cgRectValue.height
        }
        let keyboardHeight = newHeight  + 10 // **10 is bottom margin of View**  and **this newHeight will be keyboard height**
        UIView.animate(withDuration: duration,
                       delay: TimeInterval(0),
                       options: animationCurve,
                       animations: {
                        self.view.textViewBottomConstraint.constant = keyboardHeight **//Here you can manage your view constraints for animated show**
                        self.view.layoutIfNeeded() },
                       completion: nil)
    }
}

Opzione 2: - Funziona benissimo

func textFieldDidBeginEditing(textField: UITextField) {
        self.animateViewMoving(up: true, moveValue: 100)
}
func textFieldDidEndEditing(textField: UITextField) {
        self.animateViewMoving(up: false, moveValue: 100)
}

func animateViewMoving (up:Bool, moveValue :CGFloat){
    var movementDuration:NSTimeInterval = 0.3
    var 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()
}

Ho ricevuto questa risposta da questa fonte UITextField spostarsi verso l'alto quando la tastiera appare in Swift

IN the Swift 4 ---

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

    func textFieldDidEndEditing(_ textField: UITextField) {
        animateViewMoving(up: false, moveValue: 100)
    }
    func animateViewMoving (up:Bool, moveValue :CGFloat){
        let movementDuration:TimeInterval = 0.3
        let movement:CGFloat = ( up ? -moveValue : moveValue)
        UIView.beginAnimations( "animateView", context: nil)
        UIView.setAnimationBeginsFromCurrentState(true)
        UIView.setAnimationDuration(movementDuration ) 
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: movement)
        UIView.commitAnimations()
    }

1
@ Jogendra.Com, grazie per il tuo duro lavoro, ma funziona meglio su iPhone 5,4s e 6.Ma, come posso disabilitarlo su iPhone 6plus e iPad (quelli superiori)
Thiha Aung

Se si utilizza l'opzione 1, assicurarsi di aggiungere un vincolo IBOutlet. Creerai un vincolo che vuoi ridimensionare usando Auto-layout, quindi trascinalo sul viewcontroller per creare un IBOutlet che ho indicato come self.iboutletConstraint.constant nella funzione animate. Inoltre, questo non ri-regola la presa nascondendo la tastiera, l'ho gestito ripristinando il vincolo al suo valore originale.
Hammad Tariq,

21

Adoro il codice Swift pulito. Quindi ecco il codice più stretto che potrei trovare per spostare una vista di testo su / giù con la tastiera. Attualmente funziona in un'app di produzione iOS8 / 9 Swift 2.

AGGIORNAMENTO (marzo 2016): ho appena rafforzato il mio codice precedente il più possibile. Inoltre, ci sono un sacco di risposte popolari qui che codificano in altezza l'altezza della tastiera e i parametri di animazione. Non è necessario, per non parlare del fatto che i numeri in queste risposte non sempre si allineano con i valori effettivi che vedo sul mio 6s + iOS9 (altezza della tastiera di 226, durata di 0,25 e curva di animazione di 7). In ogni caso, non è quasi più un codice aggiuntivo per ottenere quei valori direttamente dal sistema. Vedi sotto.

override func viewDidLoad() {
    super.viewDidLoad()

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

func animateWithKeyboard(notification: NSNotification) {

    // Based on both Apple's docs and personal experience, 
    // I assume userInfo and its documented keys are available.
    // If you'd like, you can remove the forced unwrapping and add your own default values.

    let userInfo = notification.userInfo!
    let keyboardHeight = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().height
    let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as! Double
    let curve = userInfo[UIKeyboardAnimationCurveUserInfoKey] as! UInt
    let moveUp = (notification.name == UIKeyboardWillShowNotification)

    // baseContraint is your Auto Layout constraint that pins the
    // text view to the bottom of the superview.

    baseConstraint.constant = moveUp ? -keyboardHeight : 0

    let options = UIViewAnimationOptions(rawValue: curve << 16)
    UIView.animateWithDuration(duration, delay: 0, options: options,
        animations: {
            self.view.layoutIfNeeded()
        },
        completion: nil
    )

}

NOTA: questo codice copre la maggior parte dei commenti / casi generali. Tuttavia, potrebbe essere necessario più codice per gestire diversi orientamenti e / o tastiere personalizzate Ecco un articolo approfondito su come lavorare con la tastiera iOS. Se è necessario gestire ogni scenario, questo può essere d'aiuto.


Sembra essere Swift 1.1 e penso che non verrà compilato in Swift 1.2 perché lo usa asper i cast di forza. as!potrebbe funzionare, ma come puoi vedere altrove in questa pagina, evito di forzare i lanci e di forzare lo scartamento.
Joseph Lord

Compila ora in Swift 1.2. E ho aggiunto un commento al codice in merito: lo scartamento forzato. Saluti.
scootermg

Ops. Intendevo Swift 2.
scootermg

A seconda di come hai collegato il tuo baseConstraintpotrebbe esserebaseConstraint.constant = moveUp ? keyboardHeight : 0 invece di baseConstraint.constant = moveUp ? -keyboardHeight : 0.
limfinità

15

Modifica : raccomando una soluzione più semplice e pulita. Basta cambiare la classe del vincolo di spaziatura inferiore in KeyboardLayoutConstraint . Si espanderà automaticamente all'altezza della tastiera.


Questa è una versione migliorata della risposta di @JosephLord.

Come testato su iOS 8.3 iPad Simulator, Portrait. Xcode6.3 beta4, ho scoperto che la sua risposta non funziona quando la tastiera si nasconde perché lo UIKeyboardFrameEndUserInfoKeyè "NSRect: {{0, 1024}, {768, 264}}";. L'altezza non è mai0 .

Questo risale all'uso del tradizionale UIKeyboardWillShowNotification e UIKeyboardWillHideNotificationa capire meglio quando la tastiera si nasconde piuttosto che fare affidamento sull'altezza del frame finale. UIKeyboardWillShowNotificationviene inoltre inviato quando viene cambiata la cornice della tastiera, quindi dovrebbe coprire tutti i casi d'uso.

    // You have to set this up in storyboard first!. 
    // It's a vertical spacing constraint between view and bottom of superview.
    @IBOutlet weak var bottomSpacingConstraint: NSLayoutConstraint! 

    override func viewDidLoad() {
        super.viewDidLoad()

        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardNotification:"), name:UIKeyboardWillShowNotification, object: nil);
        NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardNotification:"), name:UIKeyboardWillHideNotification, object: nil);
    }

    deinit {
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }

    func keyboardNotification(notification: NSNotification) {

        let isShowing = notification.name == UIKeyboardWillShowNotification

        if let userInfo = notification.userInfo {
            let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
            let endFrameHeight = endFrame?.size.height ?? 0.0
            let duration:NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
            let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
            let animationCurveRaw = animationCurveRawNSN?.unsignedLongValue ?? UIViewAnimationOptions.CurveEaseInOut.rawValue
            let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
            self.bottomSpacingConstraint?.constant = isShowing ? endFrameHeight : 0.0
            UIView.animateWithDuration(duration,
                delay: NSTimeInterval(0),
                options: animationCurve,
                animations: { self.view.layoutIfNeeded() },
                completion: nil)
        }
    }

Potresti spiegare per favore la tua modifica? Non riuscivo a farlo funzionare. Ho un UIScrollView con un pulsante in basso. Ho impostato la classe sul margine inferiore in basso.
schw4ndi,

@ schw4ndi a quali punti di vista sono legati i tuoi vincoli inferiori? dovrebbe connettere la parte inferiore della vista di scorrimento alla parte inferiore della vista di scorrimento di quella vista.
Hlung,

Oh grazie, ho avuto il vincolo tra il pulsante e lo scrollView
schw4ndi

9

sto lavorando con Swift 4 e ho risolto questo problema senza usare alcun aspetto di vincolo inferiore il mio codice è qui. Funziona davvero sul mio caso

1) Aggiungi Notification Observer in effettivamente caricato

override func viewDidLoad() {
        super.viewDidLoad()
        setupManager()
        // Do any additional setup after loading the view.
        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)
    }

2) Rimuovere Notification Observer come

deinit {
        NotificationCenter.default.removeObserver(self)
    }

3) Aggiungi tastiera mostra / nascondi metodi simili

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

@objc func keyboardWillHide(notification: NSNotification) {
        if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
            UIView.animate(withDuration: 0.1, animations: { () -> Void in
                self.view.frame.origin.y += keyboardSize.height
                self.view.layoutIfNeeded()
            })
        }
    }

4) Aggiungi delegato textfeild e aggiungi tocchi Metodi di inizio. Utile per nascondere la tastiera quando si tocca al di fuori del textfeild sullo schermo

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

    }

deve essereUIKeyboardFrameEndUserInfoKey
Micro

1
NSNotification.Name.UIKeyboardWillShow è stato rinominato UIResponder.keyboardWillShowNotificationanche UIKeyboardFrameBeginUserInfoKey inUIResponder.keyboardFrameBeginUserInfoKey
smj

7

Questa è una versione migliorata di @JosephLord e della risposta di @ Hlung. Può applicarsi indipendentemente dalla barra delle schede. E ripristinerebbe perfettamente la vista che viene spostata dalla tastiera nella posizione originale.

// You have to set this up in storyboard first!. 
// It's a vertical spacing constraint between view and bottom of superview.
@IBOutlet weak var bottomSpacingConstraint: NSLayoutConstraint! 

override func viewDidLoad() {
        super.viewDidLoad()            

        //    Receive(Get) Notification
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardNotification:", name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardNotification:", name: UIKeyboardWillHideNotification, object: nil)


        self.originalConstraint = self.keyboardHeightLayoutConstraint?.constant //for original coordinate.
}

func keyboardNotification(notification: NSNotification) {
        let isShowing = notification.name == UIKeyboardWillShowNotification

        var tabbarHeight: CGFloat = 0
        if self.tabBarController? != nil {
            tabbarHeight = self.tabBarController!.tabBar.frame.height
        }
        if let userInfo = notification.userInfo {
            let endFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
            let duration:NSTimeInterval = (userInfo[UIKeyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
            let animationCurveRawNSN = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? NSNumber
            let animationCurveRaw = animationCurveRawNSN?.unsignedLongValue ?? UIViewAnimationOptions.CurveEaseInOut.rawValue
            let animationCurve:UIViewAnimationOptions = UIViewAnimationOptions(rawValue: animationCurveRaw)
            self.keyboardHeightLayoutConstraint?.constant = isShowing ? (endFrame!.size.height - tabbarHeight) : self.originalConstraint!
            UIView.animateWithDuration(duration,
                delay: NSTimeInterval(0),
                options: animationCurve,
                animations: { self.view.layoutIfNeeded() },
                completion: nil)
        }
}

6

Il modo più semplice che non richiede alcun codice:

  1. Scarica KeyboardLayoutConstraint.swift e aggiungi (trascina e rilascia) il file nel tuo progetto, se non stai già utilizzando il framework di animazione Spring.
  2. Nello storyboard, crea un vincolo inferiore per l'oggetto / vista / campo di testo, seleziona il vincolo (fai doppio clic su di esso) e in Identity Inspector, cambia la sua classe da NSLayoutConstraint a KeyboardLayoutConstraint.
  3. Fatto!

L'oggetto si sposterà automaticamente verso l'alto con la tastiera, in sincronia.


2
Ottima soluzione! Ma hai bisogno della Safe Area. Bottom come primo elemento del vincolo (non ha funzionato per me quando era il secondo elemento). E ha funzionato meglio con la costante impostata su 0 in quanto conserva la costante e la regola, piuttosto che spostarla abbastanza lontano da mostrare il campo e la tastiera.
Mythlandia,

Hai la versione KeyboardLayoutConstraint swift4?
jeet.chanchawat,

6

Ho creato un protocollo Swift 3 per gestire l'aspetto / scomparsa della tastiera

import UIKit

protocol KeyboardHandler: class {

var bottomConstraint: NSLayoutConstraint! { get set }

    func keyboardWillShow(_ notification: Notification)
    func keyboardWillHide(_ notification: Notification)
    func startObservingKeyboardChanges()
    func stopObservingKeyboardChanges()
}


extension KeyboardHandler where Self: UIViewController {

    func startObservingKeyboardChanges() {

        // NotificationCenter observers
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillShow, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillShow(notification)
        }

        // Deal with rotations
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillShow(notification)
        }

        // Deal with keyboard change (emoji, numerical, etc.)
        NotificationCenter.default.addObserver(forName: NSNotification.Name.UITextInputCurrentInputModeDidChange, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillShow(notification)
        }

        NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillHide, object: nil, queue: nil) { [weak self] notification in
          self?.keyboardWillHide(notification)
        }
    }


    func keyboardWillShow(_ notification: Notification) {

      let verticalPadding: CGFloat = 20 // Padding between the bottom of the view and the top of the keyboard

      guard let value = notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue else { return }
      let keyboardHeight = value.cgRectValue.height

      // Here you could have more complex rules, like checking if the textField currently selected is actually covered by the keyboard, but that's out of this scope.
      self.bottomConstraint.constant = keyboardHeight + verticalPadding

      UIView.animate(withDuration: 0.1, animations: { () -> Void in
          self.view.layoutIfNeeded()
      })
  }


  func keyboardWillHide(_ notification: Notification) {
      self.bottomConstraint.constant = 0

      UIView.animate(withDuration: 0.1, animations: { () -> Void in
          self.view.layoutIfNeeded()
      })
  }


  func stopObservingKeyboardChanges() {
      NotificationCenter.default.removeObserver(self)
  }

}

Quindi, per implementarlo in un UIViewController, procedi come segue:

  • lasciare che viewController sia conforme a questo protocollo:

    class FormMailVC: UIViewControlle, KeyboardHandler {
  • inizia ad osservare le modifiche della tastiera in viewWillAppear:

    // MARK: - View controller life cycle
    override func viewWillAppear(_ animated: Bool) {
      super.viewWillAppear(animated)
      startObservingKeyboardChanges()
    }
  • smettere di osservare le modifiche della tastiera in viewWillDisappear:

    override func viewWillDisappear(_ animated: Bool) {
      super.viewWillDisappear(animated)
      stopObservingKeyboardChanges()
    }
  • creare un IBOutlet per il vincolo inferiore dallo storyboard:

    // NSLayoutConstraints
    @IBOutlet weak var bottomConstraint: NSLayoutConstraint!

    (Consiglio di avere tutta la tua interfaccia utente incorporata in un "contentView" e di collegare a questa proprietà il vincolo inferiore da questo contentView alla guida di layout in basso) Vincolo inferiore della vista contenuto

  • modifica la priorità del vincolo del vincolo superiore su 250 (basso)

Vincolo principale della visualizzazione contenuto

Questo per far scorrere l'intero contenuto verso l'alto quando appare la tastiera. La priorità deve essere inferiore rispetto a qualsiasi altra priorità di vincolo nelle sottoview, comprese le priorità di abbracciamento del contenuto / priorità di resistenza alla compressione del contenuto.

  • Assicurati che il tuo Autolayout abbia abbastanza vincoli per determinare come dovrebbe scorrere verso l'alto il contenuto.

Potrebbe essere necessario aggiungere un vincolo "maggiore di uguale" per questo: vincolo "maggiore di uguale"

Ed ecco qua! Senza tastiera

Con tastiera


Funziona se metti anche "Relation = Equal", senza avvisi.
Valtoni Boaventura,

Se metti una relazione uguale, potrebbe funzionare solo in situazioni specifiche. In altri potresti avere un avviso di incoerenza del layout automatico. Dipende dal tuo layout. Ecco perché ho detto "potrebbe essere necessario".
Frédéric Adda,

Ok Frédéric, d'accordo. È stata una bella soluzione!
Valtoni Boaventura,

mancanteimport UIKit
Mirko il

6

Tale semplice UIViewController estensione può essere utilizzata

//MARK: - Observers
extension UIViewController {

    func addObserverForNotification(notificationName: String, actionBlock: (NSNotification) -> Void) {
        NSNotificationCenter.defaultCenter().addObserverForName(notificationName, object: nil, queue: NSOperationQueue.mainQueue(), usingBlock: actionBlock)
    }

    func removeObserver(observer: AnyObject, notificationName: String) {
        NSNotificationCenter.defaultCenter().removeObserver(observer, name: notificationName, object: nil)
    }
}

//MARK: - Keyboard observers
extension UIViewController {

    typealias KeyboardHeightClosure = (CGFloat) -> ()

    func addKeyboardChangeFrameObserver(willShow willShowClosure: KeyboardHeightClosure?,
        willHide willHideClosure: KeyboardHeightClosure?) {
            NSNotificationCenter.defaultCenter().addObserverForName(UIKeyboardWillChangeFrameNotification,
                object: nil, queue: NSOperationQueue.mainQueue(), usingBlock: { [weak self](notification) in
                    if let userInfo = notification.userInfo,
                        let frame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue(),
                        let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double,
                        let c = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt,
                        let kFrame = self?.view.convertRect(frame, fromView: nil),
                        let kBounds = self?.view.bounds {

                            let animationType = UIViewAnimationOptions(rawValue: c)
                            let kHeight = kFrame.size.height
                            UIView.animateWithDuration(duration, delay: 0, options: animationType, animations: {
                                if CGRectIntersectsRect(kBounds, kFrame) { // keyboard will be shown
                                    willShowClosure?(kHeight)
                                } else { // keyboard will be hidden
                                    willHideClosure?(kHeight)
                                }
                                }, completion: nil)
                    } else {
                            print("Invalid conditions for UIKeyboardWillChangeFrameNotification")
                    }
            })
    }

    func removeKeyboardObserver() {
        removeObserver(self, notificationName: UIKeyboardWillChangeFrameNotification)
    }
}

Esempio di utilizzo

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

        removeKeyboardObserver()
    }

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

    addKeyboardChangeFrameObserver(willShow: { [weak self](height) in
        //Update constraints here
        self?.view.setNeedsUpdateConstraints()
        }, willHide: { [weak self](height) in
        //Reset constraints here
        self?.view.setNeedsUpdateConstraints()
    })
}

Soluzione Swift 4

//MARK: - Observers
extension UIViewController {

  func addObserverForNotification(_ notificationName: Notification.Name, actionBlock: @escaping (Notification) -> Void) {
    NotificationCenter.default.addObserver(forName: notificationName, object: nil, queue: OperationQueue.main, using: actionBlock)
  }

  func removeObserver(_ observer: AnyObject, notificationName: Notification.Name) {
    NotificationCenter.default.removeObserver(observer, name: notificationName, object: nil)
  }
}

//MARK: - Keyboard handling
extension UIViewController {

  typealias KeyboardHeightClosure = (CGFloat) -> ()

  func addKeyboardChangeFrameObserver(willShow willShowClosure: KeyboardHeightClosure?,
                                      willHide willHideClosure: KeyboardHeightClosure?) {
    NotificationCenter.default.addObserver(forName: NSNotification.Name.UIKeyboardWillChangeFrame,
                                           object: nil, queue: OperationQueue.main, using: { [weak self](notification) in
                                            if let userInfo = notification.userInfo,
                                              let frame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
                                              let duration = userInfo[UIKeyboardAnimationDurationUserInfoKey] as? Double,
                                              let c = userInfo[UIKeyboardAnimationCurveUserInfoKey] as? UInt,
                                              let kFrame = self?.view.convert(frame, from: nil),
                                              let kBounds = self?.view.bounds {

                                              let animationType = UIViewAnimationOptions(rawValue: c)
                                              let kHeight = kFrame.size.height
                                              UIView.animate(withDuration: duration, delay: 0, options: animationType, animations: {
                                                if kBounds.intersects(kFrame) { // keyboard will be shown
                                                  willShowClosure?(kHeight)
                                                } else { // keyboard will be hidden
                                                  willHideClosure?(kHeight)
                                                }
                                              }, completion: nil)
                                            } else {
                                              print("Invalid conditions for UIKeyboardWillChangeFrameNotification")
                                            }
    })
  }

  func removeKeyboardObserver() {
    removeObserver(self, notificationName: NSNotification.Name.UIKeyboardWillChangeFrame)
  }
}

Rapido 4.2

//MARK: - Keyboard handling
extension UIViewController {

    func addObserverForNotification(_ notificationName: Notification.Name, actionBlock: @escaping (Notification) -> Void) {
        NotificationCenter.default.addObserver(forName: notificationName, object: nil, queue: OperationQueue.main, using: actionBlock)
    }

    func removeObserver(_ observer: AnyObject, notificationName: Notification.Name) {
        NotificationCenter.default.removeObserver(observer, name: notificationName, object: nil)
    }

    typealias KeyboardHeightClosure = (CGFloat) -> ()

    func removeKeyboardObserver() {
        removeObserver(self, notificationName: UIResponder.keyboardWillChangeFrameNotification)
    }

    func addKeyboardChangeFrameObserver(willShow willShowClosure: KeyboardHeightClosure?,
                                        willHide willHideClosure: KeyboardHeightClosure?) {
        NotificationCenter.default.addObserver(forName: UIResponder.keyboardWillChangeFrameNotification,
                                               object: nil, queue: OperationQueue.main, using: { [weak self](notification) in
                                                if let userInfo = notification.userInfo,
                                                    let frame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,
                                                    let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double,
                                                    let c = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt,
                                                    let kFrame = self?.view.convert(frame, from: nil),
                                                    let kBounds = self?.view.bounds {

                                                    let animationType = UIView.AnimationOptions(rawValue: c)
                                                    let kHeight = kFrame.size.height
                                                    UIView.animate(withDuration: duration, delay: 0, options: animationType, animations: {
                                                        if kBounds.intersects(kFrame) { // keyboard will be shown
                                                            willShowClosure?(kHeight)
                                                        } else { // keyboard will be hidden
                                                            willHideClosure?(kHeight)
                                                        }
                                                    }, completion: nil)
                                                } else {
                                                    print("Invalid conditions for UIKeyboardWillChangeFrameNotification")
                                                }
        })
    }
}

2
molto più "veloce" rispetto alle altre soluzioni / funziona alla grande / riutilizzabile in ogni controller senza riscrivere tutto -> sicuramente il migliore qui :)
Tib

Ho provato ma nessuna possibilità, UIScrollView è necessario o cosa?
Erdemgc,

@erdemgc hai visto Esempio di utilizzo? Tutto ciò che serve è solo UIViewControlller + addKeyboardChangeFrameObserver e quindi non dimenticare di rimuoverlo
ale_stro

Il removeKeyboardObserver()metodo qui non rimuove effettivamente l'osservatore. Se non lo chiami, vedrai a Invalid conditions for UIKeyboardWillChangeFrameNotificationnella console dal metodo add. Se lo chiami, vedrai lo stesso errore, il che significa che l'osservatore non viene rimosso. La documentazione afferma "Per annullare la registrazione delle osservazioni, si passa l'oggetto restituito da questo metodo a removeObserver(_:)". Quindi, ciò che fai invece è salvare l'oggetto restituito da quel metodo, quindi passarlo quando vuoi rimuovere l'osservatore.
Huy-Anh Hoang,

In realtà, una volta caricata la vista a scorrimento, viene associato un valore a bound e non è possibile rilevare se una tastiera verrà nascosta se la cornice della tastiera si interseca con il limite.
James Kim,

5

Puoi usare questa libreria e solo una riga di codice in appDidFinishedLaunching e il gioco è fatto.

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

    IQKeyboardManager.sharedManager().enable = true
    return true
}

IQKeyboardManager - regola la visualizzazione ogni volta che la tastiera appare link - https://github.com/hackiftekhar/IQKeyboardManager


4
struct MoveKeyboard {
    static let KEYBOARD_ANIMATION_DURATION : CGFloat = 0.3
    static let MINIMUM_SCROLL_FRACTION : CGFloat = 0.2;
    static let MAXIMUM_SCROLL_FRACTION : CGFloat = 0.8;
    static let PORTRAIT_KEYBOARD_HEIGHT : CGFloat = 216;
    static let LANDSCAPE_KEYBOARD_HEIGHT : CGFloat = 162;
}


  func textFieldDidBeginEditing(textField: UITextField) {
    let textFieldRect : CGRect = self.view.window!.convertRect(textField.bounds, fromView: textField)
    let viewRect : CGRect = self.view.window!.convertRect(self.view.bounds, fromView: self.view)

    let midline : CGFloat = textFieldRect.origin.y + 0.5 * textFieldRect.size.height
    let numerator : CGFloat = midline - viewRect.origin.y - MoveKeyboard.MINIMUM_SCROLL_FRACTION * viewRect.size.height
    let denominator : CGFloat = (MoveKeyboard.MAXIMUM_SCROLL_FRACTION - MoveKeyboard.MINIMUM_SCROLL_FRACTION) * viewRect.size.height
    var heightFraction : CGFloat = numerator / denominator

    if heightFraction < 0.0 {
        heightFraction = 0.0
    } else if heightFraction > 1.0 {
        heightFraction = 1.0
    }

    let orientation : UIInterfaceOrientation = UIApplication.sharedApplication().statusBarOrientation
    if (orientation == UIInterfaceOrientation.Portrait || orientation == UIInterfaceOrientation.PortraitUpsideDown) {
        animateDistance = floor(MoveKeyboard.PORTRAIT_KEYBOARD_HEIGHT * heightFraction)
    } else {
        animateDistance = floor(MoveKeyboard.LANDSCAPE_KEYBOARD_HEIGHT * heightFraction)
    }

    var viewFrame : CGRect = self.view.frame
    viewFrame.origin.y -= animateDistance

    UIView.beginAnimations(nil, context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(NSTimeInterval(MoveKeyboard.KEYBOARD_ANIMATION_DURATION))

    self.view.frame = viewFrame

    UIView.commitAnimations()
}


func textFieldDidEndEditing(textField: UITextField) {
    var viewFrame : CGRect = self.view.frame
    viewFrame.origin.y += animateDistance

    UIView.beginAnimations(nil, context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)

    UIView.setAnimationDuration(NSTimeInterval(MoveKeyboard.KEYBOARD_ANIMATION_DURATION))

    self.view.frame = viewFrame

    UIView.commitAnimations()

}

E infine, poiché stiamo usando i metodi dei delegati

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

refactored dall'uso di object-c http://www.cocoawithlove.com/2008/10/sliding-uitextfields-around-to-avoid.html


Questa soluzione ha funzionato per me, anche se ho dovuto fare un po 'di lavoro aggiuntivo: dichiarare un var animateDistance: CGFloat!vantaggio che ho dovuto gestire UIKeyboardWillHideNotification per quando l'utente preme il pulsante Nascondi tastiera.
Rhuantavan,

4

Un'altra soluzione che non dipende da autolayout, vincoli o punti vendita. Ciò di cui hai bisogno sono i tuoi campi in una vista di scorrimento.

override func viewDidLoad() {
    super.viewDidLoad()

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

func makeSpaceForKeyboard(notification: NSNotification) {
    let info = notification.userInfo!
    let keyboardHeight:CGFloat = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().size.height
    let duration:Double = info[UIKeyboardAnimationDurationUserInfoKey] as! Double

    if notification.name == UIKeyboardWillShowNotification {
        UIView.animateWithDuration(duration, animations: { () -> Void in
            var frame = self.view.frame
            frame.size.height = frame.size.height - keyboardHeight
            self.view.frame = frame
        })
    } else {
        UIView.animateWithDuration(duration, animations: { () -> Void in
            var frame = self.view.frame
            frame.size.height = frame.size.height + keyboardHeight
            self.view.frame = frame
        })
    }

}

1
Mostra lo schermo nero dopo aver UIKeyboardWillShowNotificationchiamato.
Sachin Kumaram,

4

Ecco la mia versione per una soluzione per Swift 2.2:

Primo registro per Tastiera Mostra / Nascondi notifiche

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

Quindi, nei metodi corrispondenti a tali notifiche, spostare la vista principale verso l'alto o verso il basso

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

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

Il trucco sta nella parte "keyboardWillShow" che riceve le chiamate ogni volta che "Barra suggerimenti QuickType" viene espansa o compressa. Quindi impostiamo sempre la coordinata y della vista principale che è uguale al valore negativo dell'altezza totale della tastiera (con o senza la parte "Barra di QuickType").

Alla fine non dimenticare di rimuovere gli osservatori

deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}

3

Quella che segue è una soluzione semplice, per cui il campo di testo ha un vincolo che lo lega alla guida di layout inferiore. Aggiunge semplicemente l'altezza della tastiera alla costante del vincolo.

// This constraint ties the text field to the bottom layout guide
@IBOutlet var textFieldToBottomLayoutGuideConstraint: NSLayoutConstraint!

override func viewDidLoad() {
    super.viewDidLoad()

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

func keyboardWillShow(sender: NSNotification) {
    if let keyboardSize = (sender.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.textFieldToBottomLayoutGuideConstraint?.constant += keyboardSize.height
    }
}

func keyboardWillHide(sender: NSNotification) {
    if let keyboardSize = (sender.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
        self.textFieldToBottomLayoutGuideConstraint?.constant -= keyboardSize.height
    }
}

3

Beh, penso che potrei essere troppo tardi, ma ho trovato un'altra versione semplice della risposta di Saqib. Sto usando Autolayout con vincoli. Ho una piccola vista all'interno di un'altra vista principale con campi nome utente e password. Invece di cambiare la coordinata y della vista, sto salvando il valore del vincolo originale in una variabile e cambiando la costante del vincolo in un valore e di nuovo dopo che la tastiera si è chiusa, sto impostando il vincolo su quello originale. In questo modo si evita il problema della risposta di Saqib, (la vista continua a salire e non si ferma). Di seguito è il mio codice ...

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);
    self.originalConstraint = self.centerYConstraint.constant
  }

  func keyboardWillShow(sender: NSNotification) {
    self.centerYConstraint.constant += 30
  }

  func keyboardWillHide(sender: NSNotification) {
    self.centerYConstraint.constant = self.originalConstraint
  }

All'interno del metodo keyboardWillShow controlla la condizione se self.centerYConstraint.constant == self.originalCenterYConstraint ha una riga di codice tra questa condizione. OriginalCenterYContraint è il valore originale di centerYContraint che sto memorizzando in viewdidload. Questo ha funzionato per me.
Sashi,

3

Risposta rapida 4.x, unendo le risposte di @Joseph Lord e @Isuru. bottomConstraintrappresenta il vincolo inferiore della vista che ti interessa spostare.

override func viewDidLoad() {
    // Call super
    super.viewDidLoad()

    // Subscribe to keyboard notifications
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(keyboardNotification(notification:)),
                                           name: UIResponder.keyboardWillChangeFrameNotification,
                                           object: nil)        
}


deinit {
    NotificationCenter.default.removeObserver(self)
}


@objc func keyboardNotification(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        // Get keyboard frame
        let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue

        // Set new bottom constraint constant
        let bottomConstraintConstant = keyboardFrame.origin.y >= UIScreen.main.bounds.size.height ? 0.0 : keyboardFrame.size.height

        // Set animation properties
        let duration = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0
        let animationCurveRawNSN = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber
        let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue
        let animationCurve = UIView.AnimationOptions(rawValue: animationCurveRaw)

        // Animate the view you care about
        UIView.animate(withDuration: duration, delay: 0, options: animationCurve, animations: {
            self.bottomConstraint.constant = bottomConstraintConstant
            self.view.layoutIfNeeded()
        }, completion: nil)
    }
}

2

Ho fatto nel modo seguente:

Ciò è utile quando viene visualizzata la superview del campo di testo

class AdminLoginViewController: UIViewController,
UITextFieldDelegate{

    @IBOutlet weak var txtUserName: UITextField!
    @IBOutlet weak var txtUserPassword: UITextField!
    @IBOutlet weak var btnAdminLogin: UIButton!

    private var activeField : UIView?

    var param:String!
    var adminUser : Admin? = nil
    var kbHeight: CGFloat!

    override func viewDidLoad()
    {
        self.addKeyBoardObserver()
        self.addGestureForHideKeyBoard()
    }

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

    func addGestureForHideKeyBoard()
    {
        let tapGesture = UITapGestureRecognizer(target: self, action: Selector("hideKeyboard"))
        tapGesture.cancelsTouchesInView = false
        view.addGestureRecognizer(tapGesture)
    }

    func hideKeyboard() {
        self.view.endEditing(true)
    }

    func addKeyBoardObserver(){

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

    func removeObserver(){
        NSNotificationCenter.defaultCenter().removeObserver(self)
    }

    //MARK:- textfiled Delegate

    func textFieldShouldBeginEditing(textField: UITextField) -> Bool
    {
         activeField = textField

        return true
    }
    func textFieldShouldEndEditing(textField: UITextField) -> Bool
    {
        if activeField == textField
        {
            activeField = nil
        }

        return true
    }

    func textFieldShouldReturn(textField: UITextField) -> Bool {

        if txtUserName == textField
        {
            txtUserPassword.becomeFirstResponder()
        }
        else if (textField == txtUserPassword)
        {
            self.btnAdminLoginAction(nil)
        }
        return true;
    }

    func willChangeKeyboardFrame(aNotification : NSNotification)
    {
       if self.activeField != nil && self.activeField!.isFirstResponder()
    {
        if let keyboardSize =  (aNotification.userInfo![UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue()
        {
            let dy = (self.activeField?.superview?.convertRect((self.activeField?.frame)!, toView: view).origin.y)!

            let height = (self.view.frame.size.height - keyboardSize.size.height)

            if dy > height
            {
                var frame = self.view.frame

                frame.origin.y = -((dy - height) + (self.activeField?.frame.size.height)! + 20)

                self.view.frame = frame
            }
        }
    }
    else
    {
        var frame = self.view.frame
        frame.origin.y = 0
        self.view.frame = frame
    }
    } }

2
    func registerForKeyboardNotifications(){
        //Keyboard
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWasShown), name: UIKeyboardDidShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(keyboardWillBeHidden), name: UIKeyboardDidHideNotification, object: nil)


    }
    func deregisterFromKeyboardNotifications(){

        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)

    }
    func keyboardWasShown(notification: NSNotification){

        let userInfo: NSDictionary = notification.userInfo!
        let keyboardInfoFrame = userInfo.objectForKey(UIKeyboardFrameEndUserInfoKey)?.CGRectValue()

        let windowFrame:CGRect = (UIApplication.sharedApplication().keyWindow!.convertRect(self.view.frame, fromView:self.view))

        let keyboardFrame = CGRectIntersection(windowFrame, keyboardInfoFrame!)

        let coveredFrame = UIApplication.sharedApplication().keyWindow!.convertRect(keyboardFrame, toView:self.view)

        let contentInsets = UIEdgeInsetsMake(0, 0, (coveredFrame.size.height), 0.0)
        self.scrollViewInAddCase .contentInset = contentInsets;
        self.scrollViewInAddCase.scrollIndicatorInsets = contentInsets;
        self.scrollViewInAddCase.contentSize = CGSizeMake((self.scrollViewInAddCase.contentSize.width), (self.scrollViewInAddCase.contentSize.height))

    }
    /**
     this method will fire when keyboard was hidden

     - parameter notification: contains keyboard details
     */
    func keyboardWillBeHidden (notification: NSNotification) {

        self.scrollViewInAddCase.contentInset = UIEdgeInsetsZero
        self.scrollViewInAddCase.scrollIndicatorInsets = UIEdgeInsetsZero

    }

1
Usa il codice sopra per spostare il campo di testo sopra la tastiera in Swift 2.2 funzionerà bene. spero che possa aiutare qualcuno.
Kamalkumar.E

1

Ho fatto nel modo seguente:

class SignInController: UIViewController , UITextFieldDelegate {

@IBOutlet weak var scrollView: UIScrollView!

// outlet declartion
@IBOutlet weak var signInTextView: UITextField!

var kbHeight: CGFloat!

/**
*
* @method viewDidLoad
*
*/

override func viewDidLoad() {
    super.viewDidLoad()

    self.signInTextView.delegate = self

}// end viewDidLoad

/**
*
* @method viewWillAppear
*
*/

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

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

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

}// end viewWillAppear

/**
*
* @method viewDidAppear
*
*/

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)


}// end viewDidAppear

/**
*
* @method viewWillDisappear
*
*/
override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

/**
*
* @method textFieldShouldReturn
* retun the keyboard value
*
*/

// MARK -
func textFieldShouldReturn(textField: UITextField) -> Bool {
    signInTextView.resignFirstResponder()
    return true;

}// end textFieldShouldReturn

// MARK - keyboardWillShow
func keyboardWillShow(notification: NSNotification) {
    if let userInfo = notification.userInfo {
        if let keyboardSize =  (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
            kbHeight = keyboardSize.height
            self.animateTextField(true)
        }
    }
}// end keyboardWillShow

// MARK - keyboardWillHide
func keyboardWillHide(notification: NSNotification) {
    self.animateTextField(false)
}// end keyboardWillHide

// MARK - animateTextField
func animateTextField(up: Bool) {
    var movement = (up ? -kbHeight : kbHeight)

    UIView.animateWithDuration(0.3, animations: {
        self.view.frame = CGRectOffset(self.view.frame, 0, movement)
    })
}// end animateTextField

/**
*
* @method didReceiveMemoryWarning
*
*/

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.

}// end didReceiveMemoryWarning


}// end SignInController

1

Se sei come me che ha provato tutte le soluzioni di cui sopra e il tuo problema non è ancora risolto, ho un'ottima soluzione per te che funziona come un fascino. Innanzitutto voglio chiarire alcune cose su alcune delle soluzioni sopra menzionate.

  1. Nel mio caso IQkeyboardmanager funzionava solo quando non è stato applicato alcun layout automatico agli elementi, se applicato, IQkeyboard manager non funzionerà come pensiamo.
  2. Stessa cosa con il movimento verso l'alto di sé. Vista.
  3. ho indossato un header c obiettivo con un rapido supporto per spingere UITexfield verso l'alto quando l'utente fa clic su di esso, risolvendo il problema della tastiera che copre l'UITextfield: https://github.com/coolvasanth/smart_keyboard .
  4. Chi ha un livello intermedio o superiore nello sviluppo di app iOS può facilmente comprendere il repository e implementarlo. Ti auguro il meglio

1

Ecco una soluzione generica per tutti i passaggi di TextField -

1) Creare un ViewController comune che viene esteso da altri ViewController

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.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        if self.view.frame.origin.y == 0 {
            self.view.frame.origin.y -= getMoveableDistance(keyboarHeight: keyboardSize.height)
        }
    }
}

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

//get the distance to move up the main view for the focus textfiled
func getMoveableDistance(keyboarHeight : CGFloat) ->  CGFloat{
    var y:CGFloat = 0.0
    if let activeTF = getSelectedTextField(){
        var tfMaxY = activeTF.frame.maxY
        var containerView = activeTF.superview!
        while containerView.frame.maxY != self.view.frame.maxY{
            let contViewFrm = containerView.convert(activeTF.frame, to: containerView.superview)
            tfMaxY = tfMaxY + contViewFrm.minY
            containerView = containerView.superview!
        }
        let keyboardMinY = self.view.frame.height - keyboarHeight
        if tfMaxY > keyboardMinY{
            y = (tfMaxY - keyboardMinY) + 10.0
        }
    }

    return y
}

2) Creare un'estensione di UIViewController e il TextField attualmente attivo

//get active text field

estensione UIViewController {func getSelectedTextField () -> UITextField? {

    let totalTextFields = getTextFieldsInView(view: self.view)

    for textField in totalTextFields{
        if textField.isFirstResponder{
            return textField
        }
    }

    return nil

}

func getTextFieldsInView(view: UIView) -> [UITextField] {

    var totalTextFields = [UITextField]()

    for subview in view.subviews as [UIView] {
        if let textField = subview as? UITextField {
            totalTextFields += [textField]
        } else {
            totalTextFields += getTextFieldsInView(view: subview)
        }
    }

    return totalTextFields
}

}


Per qualche motivo, avevo un problema con la funzione keyboardWillShow, la dimensione della tastiera stava diventando errata dopo il primo toggle della tastiera (il primo toggle ha il frame corretto). Ho risolto il problema modificandolo in guard let userInfo = notification.userInfo else {return} guard let keyboardSize = userInfo [UIResponder.keyboardFrameEndUserInfoKey] come? NSValue else {return} lascia keyboardFrame = keyboardSize.cgRectValue se self.view.frame.origin.y == 0 {self.view.frame.origin.y - = getMoveableDistance (keyboarHeight: keyboardFrame.height)}. se qualcuno ha avuto lo stesso problema :)
Youstanzr,

1

Molto semplice e non è necessario codificare di più. Aggiungi semplicemente il pod 'IQKeyboardManagerSwift'tuo podfile e nella tua AppDelegatepagina aggiungi il codice qui sotto.

import IQKeyboardManagerSwift

e nel didFinishLaunchingWithOptions()tipo di metodo

IQKeyboardManager.shared.enable = true

questo è tutto. controlla questo collegamento video per una migliore comprensione https://youtu.be/eOM94K1ZWN8 Spero che questo ti possa aiutare.


Funziona con TextView e come posso cambiare il titolo per la chiave di ritorno "Fine"?
tdt kien,

Vai a: - "IQKeyboardManager.m" Sostituisci questa riga (Riga n. 968): - [textField addDoneOnKeyboardWithTarget: self action: @selector (doneAction :) shouldShowPlaceholder: _shouldShowTextFieldPlaceholder] con questo: - [textField addRightButtonOnKeyTT: @ auto-azione: @selector (doneAction :) shouldShowPlaceholder: _shouldShowTextFieldPlaceholder]; E non ho ancora provato per la visualizzazione del testo, spero che questo possa aiutarti.
Raghib Arshi,

1

Codice completo per la gestione della tastiera.

        override func viewWillAppear(_ animated: Bool) {
            NotificationCenter.default.addObserver(self, selector: #selector(StoryMediaVC.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
            NotificationCenter.default.addObserver(self, selector: #selector(StoryMediaVC.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
        }
        override func viewWillDisappear(_ animated: Bool) {
            NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
            NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
        }
        @objc func keyboardWillShow(notification: NSNotification) {
            guard let userInfo = notification.userInfo else {return}
            guard let keyboardSize = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {return}
            let keyboardFrame = keyboardSize.cgRectValue

            if self.view.bounds.origin.y == 0{
                self.view.bounds.origin.y += keyboardFrame.height
            }
        }


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

0

Ho modificato un po 'la soluzione @Simpa .........

override func viewDidLoad() 
{

    super.viewDidLoad()
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("makeSpaceForKeyboard:"), name:UIKeyboardWillShowNotification, object: nil);
    NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("makeSpaceForKeyboard:"), name:UIKeyboardWillHideNotification, object: nil);
}

deinit{
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

var keyboardIsVisible = false
override func makeSpaceForKeyboard(notification: NSNotification) {

    let info = notification.userInfo!
    let keyboardHeight:CGFloat = (info[UIKeyboardFrameEndUserInfoKey] as! NSValue).CGRectValue().size.height
    let duration:Double = info[UIKeyboardAnimationDurationUserInfoKey] as! Double

    if notification.name == UIKeyboardWillShowNotification && keyboardIsVisible == false{

        keyboardIsVisible = true

        UIView.animateWithDuration(duration, animations: { () -> Void in
            var frame = self.view.frame
            frame.size.height = frame.size.height - keyboardHeight
            self.view.frame = frame
        })

    } else if keyboardIsVisible == true && notification.name == UIKeyboardWillShowNotification{

    }else {
        keyboardIsVisible = false

        UIView.animateWithDuration(duration, animations: { () -> Void in
            var frame = self.view.frame
            frame.size.height = frame.size.height + keyboardHeight
            self.view.frame = frame
        })
    }
}

0

Nessuno di loro ha funzionato e ho finito con l'uso di inserti di contenuto per spostare la mia vista verso l'alto quando appare la tastiera.

Nota: stavo usando un UITableView

Soluzione di riferimento @ offset del contenuto della tastiera che è stata interamente scritta nell'obiettivo C, la soluzione di seguito è Swift pulito.

Aggiungi l'osservatore delle notifiche @ viewDidLoad ()

NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(yourClass.keyboardWillBeShown), name:UIKeyboardWillShowNotification, object: nil);
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(yourClass.keyboardWillBeHidden), name:UIKeyboardWillHideNotification, object: nil);

Per ottenere le dimensioni della tastiera, otteniamo innanzitutto il dizionario userInfo dall'oggetto di notifica, che memorizza eventuali oggetti aggiuntivi che il nostro ricevitore potrebbe utilizzare.

Da quel dizionario possiamo ottenere l'oggetto CGRect che descrive il frame della tastiera usando il tasto UIKeyboardFrameBeginUserInfoKey.

Applica l'inserzione del contenuto per il metodo view table @ keyboardWillBeShown,

func keyboardWillBeShown(sender: NSNotification)
{        
    // Move the table view

    if let keyboardSize = (sender.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
    {
        let contentInsets = UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.height), 0.0);

        yourTableView.contentInset = contentInsets;

        yourTableView.scrollIndicatorInsets = contentInsets;
    }
}

Ripristina il metodo view @ keyboardWillBeHidden

func keyboardWillBeHidden(sender: NSNotification)
{
    // Moving back the table view back to the default position

    yourTableView.contentInset = UIEdgeInsetsZero;

    yourTableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}

Se si desidera tenere in considerazione anche l'orientamento del dispositivo, utilizzare le istruzioni condizionali per personalizzare il codice in base alle proprie esigenze.

// Portrait
UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.height), 0.0);

// Landscape
UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.width), 0.0);

0
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

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

func keyboardWillShow(_ notification:Notification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        tableView.contentInset = UIEdgeInsetsMake(0, 0, keyboardSize.height, 0)
    }
}

func keyboardWillHide(_ notification:Notification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        tableView.contentInset = UIEdgeInsetsMake(0, 0, 0, 0)
    }
}

inserisci qui la descrizione dell'immagine


0

La soluzione Swift 4 che uso, utilizza le dimensioni della tastiera. Sostituisci serverStatusStackViewcon qualunque vista ti interessi, ad es . self.view:

deinit {
    NotificationCenter.default.removeObserver(self)
}

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

@objc func keyboardWillHide(notification: NSNotification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        serverStatusStackView.frame.origin.y += keyboardSize.height
    }
}

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

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.