Mover el campo de texto cuando el teclado parece rápido

217

Estoy usando Swift para programar con iOS y estoy usando este código para mover el UITextField, pero no funciona. Llamo a la función keyboardWillShowcorrectamente, pero el campo de texto no se mueve. Estoy usando autolayout.

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")
    }
}
Pedro Manfredi
fuente
2
Guía paso a paso con archivos de proyecto: codebeaulieu.com/43/…
Dan Beaulieu
Quizás deinit y viewDidLoad no están equilibrados.
Ricardo
Basado tanto en los documentos de Apple como en su experiencia personal. Aquí está mi repositorio de git usando UIScrollView para mover TF: github.com/29satnam/MoveTextFieldWhenKeyboardAppearsSwift
Codetard

Respuestas:

315

Hay un par de mejoras por hacer en las respuestas existentes.

En primer lugar, UIKeyboardWillChangeFrameNotification es probablemente la mejor notificación, ya que maneja los cambios que no solo se muestran / ocultan, sino también los cambios debidos a cambios en el teclado (idioma, uso de teclados de terceros, etc.) y rotaciones también (pero tenga en cuenta el comentario a continuación que indica que el teclado se ocultará) También se debe manejar para admitir la conexión del teclado de hardware).

En segundo lugar, los parámetros de animación se pueden extraer de la notificación para garantizar que las animaciones se unan correctamente.

Probablemente hay opciones para limpiar este código un poco más, especialmente si se siente cómodo con el desenvolvimiento forzado del código del diccionario.

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()
    // Note that SO highlighting makes the new selector syntax (#selector()) look
    // like a comment but it isn't one
    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)
    }
}

(Editado para tener en cuenta la animación del teclado fuera de la pantalla en lugar de reducirse, según el impresionante comentario de @ Gabox a continuación)

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()
    // Note that SO highlighting makes the new selector syntax (#selector()) look
    // like a comment but it isn't one
    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)
    }
}
Joseph Lord
fuente
1
@JosephLord agradable. pero descubrí que esto no funciona cuando el teclado está oculto porque endFrame?.size.heightno es nulo. Obtuve el marco final como UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 1024}, {768, 264}}";. Se ejecutó en iOS 8.3 iPad Simulator, Portrait. Xcode6.3 beta4.
Hlung
8
si el teclado no se oculta, intente usar este código si endFrame? .origin.y> = UIScreen.mainScreen (). limits.size.height {self.keyboardHeightLayoutConstraint? .constant = 0.0} else {self.keyboardHeightLayoutConstraint? .constant = endFrame.size.height}
Gabriel Goncalves
3
keyBoardHeightLayoutConstraint es una restricción definida en InterfaceBuilder que restringe la parte inferior de la vista que desea mover / reducir a la guía de diseño inferior o la parte inferior de la vista principal para el controlador de vista. La constante se establece inicialmente en cero y se ajustará para dejar espacio para el teclado cuando este aparezca o cambie de tamaño.
Joseph Lord
2
Tenga en cuenta que .UIKeyboardWillChangeFrameno se activa cuando se conecta un teclado de hardware, aunque el teclado iOS desaparezca. También debe observar .UIKeyboardWillHidepara captar ese caso límite.
jamesk
1
@Sulthan funciona bien. Mi problema es que se está volviendo un poco más alto que el teclado. ¿Hay alguna manera de que podamos solucionar esto?
Pavlos
128

Si está utilizando el diseño automático, supongo que ha establecido el espacio inferior en la restricción de Supervisión . Si ese es el caso, simplemente tiene que actualizar el valor de la restricción. Así es como lo haces con un poco de animación.

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

El 20 codificado se agrega solo para resaltar el campo de texto sobre el teclado solo un poco. De lo contrario, el margen superior del teclado y el margen inferior del campo de texto se tocarían.

Cuando se descarta el teclado, restablezca el valor de la restricción a su valor original.

Isuru
fuente
1
¿Me puede explicar por favor cómo lo defino? ¡Gracias! siempre controlo todo en el guión gráfico
Pedro Manfredi
44
bottomConstraint es el nombre que le di a la restricción. Seleccioné la restricción, la arrastré y le creé un IBOutlet y le di ese nombre. Puede crear IBOutlets a restricciones tal como lo haría con otros elementos de la interfaz de usuario, como botones y campos de texto.
Isuru
2
Esta respuesta funcionó muy bien, excepto que la animación sucedió de inmediato para mí. Comprobar ¿Cómo animar los cambios de restricción? sobre cómo animar adecuadamente.
Adam Johns
2
@ vinbhai4u Debe registrarse para recibir UIKeyboardWillShowNotificationnotificaciones. Mire el código en la pregunta del OP.
Isuru
8
@AdamJohns Para animar el cambio de restricción, actualice la constante fuera de animateWithDurationy llame self.view.layoutIfNeeded()dentro del bloque animado.
Max
110

Una solución simple es mover la vista hacia arriba con una altura constante del teclado.

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);
Saqib Omer
fuente
44
Me gusta esta solución simple. Pero agregué un booleano KeyboardShowing ya que he movido más de un campo de texto. Solo muevo el teclado una vez mientras se muestra. Gracias.
Ken
2
en lugar de mover la vista, mueva el textView
ericgu
1
Esto mantendrá menos el valor y de la vista si el usuario cambia el idioma de entrada.
Jeffrey Neo
en lugar de cambiar el valor de self.view que hice self.myConstraint, funciona, pero la cosa es que la vista (a la que se aplica la restricción) sigue subiendo. ¿Alguien se enfrentó a este problema?
Sashi
55
En lugar de usar el self.view.frame.origin.y -= 150uso self.view.frame.origin.y = -150y en lugar de self.view.frame.origin.y += 150usar self.view.frame.origin.y = 0. Esto evita que la vista se mueva 150 cada vez que se toca un nuevo campo.
gunwin
43

Para mover su vista mientras edita el campo de texto intente esto, he aplicado esto,

Opción 1: - ** ** Actualización en Swift 5.0 y iPhone X, XR, XS y XS Max Move usando NotificationCenter

  • Registre esta notificación en func viewWillAppear(_ animated: Bool)

  • Anular el registro de esta notificación en func viewWillDisappear(_ animated: Bool)

Nota: - Si no cancelará el registro, llamará desde la clase secundaria y provocará un bloqueo u otra cosa.

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

Opción 2: - Funciona bien

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

Recibí esta respuesta de esta fuente UITextField subir cuando el teclado aparece en Swift

EN el 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()
    }
Jogendra.Com
fuente
1
@ Jogendra.Com, gracias por su arduo trabajo. Pero funciona mejor en iPhone 5,4s y 6. Pero, ¿cómo puedo desactivarlo en iPhone 6plus y iPad (superiores)
Thiha Aung
Si está utilizando la Opción 1, asegúrese de agregar una restricción IBOutlet. Creará una restricción que desea cambiar de tamaño usando el diseño automático, luego arrástrela y suéltela en el controlador de vista para crear un IBOutlet al que le hago referencia como self.iboutletConstraint.constant en la función animada. Además, esto no reajusta la salida al ocultar el teclado, lo manejé restableciendo la restricción a su valor original.
Hammad Tariq
21

Me encanta el código Swift limpio. Así que aquí está el código más estricto que se me ocurrió para mover una vista de texto hacia arriba / abajo con el teclado. Actualmente está trabajando en una aplicación de producción iOS8 / 9 Swift 2.

ACTUALIZACIÓN (marzo de 2016): acabo de ajustar mi código anterior tanto como sea posible. Además, hay un montón de respuestas populares aquí que codifican la altura del teclado y los parámetros de animación. No hay necesidad de eso, sin mencionar que los números en estas respuestas no siempre se alinean con los valores reales que veo en mi 6s + iOS9 (altura del teclado de 226, duración de 0.25 y curva de animación de 7). En cualquier caso, casi no es un código adicional para obtener esos valores directamente del sistema. Vea abajo.

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: Este código cubre la mayoría de los comentarios / caso general. Sin embargo, es posible que se necesite más código para manejar diferentes orientaciones y / o teclados personalizados. Aquí hay un artículo detallado sobre cómo trabajar con el teclado iOS. Si necesita manejar cada escenario, esto puede ayudar.

scootermg
fuente
Parece ser Swift 1.1 y creo que no se compilará en Swift 1.2 porque se usa aspara lanzamientos de fuerza. as!puede funcionar, pero como puede ver en otra parte de esta página, evito los lanzamientos forzados y el desenvolvimiento forzado.
Joseph Lord
Compila ahora en Swift 1.2. Y agregué un comentario al código re: el desenvolvimiento forzado. Salud.
scootermg
Ups Me refería a Swift 2.
scootermg
Dependiendo de cómo se vinculó baseConstraint, podría ser en baseConstraint.constant = moveUp ? keyboardHeight : 0lugar de baseConstraint.constant = moveUp ? -keyboardHeight : 0.
limfinity
15

Editar : recomiendo una solución más fácil y limpia. Simplemente cambie la clase de restricción de espacio inferior a KeyboardLayoutConstraint . Se expandirá automáticamente a la altura del teclado.


Esta es una versión mejorada de la respuesta de @JosephLord.

Según lo probado en iOS 8.3 iPad Simulator, Portrait. Xcode6.3 beta4, que encontró su respuesta no funciona cuando el teclado se esconde porque UIKeyboardFrameEndUserInfoKeyes "NSRect: {{0, 1024}, {768, 264}}";. La altura nunca es0 .

Esto vuelve a usar el tradicional UIKeyboardWillShowNotificationy UIKeyboardWillHideNotificationpara saber mejor cuándo se oculta el teclado en lugar de confiar en la altura del marco final. UIKeyboardWillShowNotificationtambién se envía cuando se cambia el marco del teclado, por lo que debe cubrir todos los casos de 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)
        }
    }
Hlung
fuente
¿Podrías explicar tu edición? No pude hacerlo funcionar. Tengo un UIScrollView con un botón en la parte inferior. Establecí la clase en la restricción de margen inferior en la parte inferior.
schw4ndi
@ schw4ndi ¿a qué vistas están vinculadas sus restricciones inferiores? debería estar conectando la parte inferior de la vista de desplazamiento con la parte inferior de la supervista de esa vista de desplazamiento.
Hlung
Oh, gracias, tenía la restricción entre el botón y el scrollView
schw4ndi
9

estoy trabajando con swift 4 y he resuelto este problema sin usar ninguna restricción inferior adicional, mi código está aquí. Realmente funciona en mi caso

1) Agregue el Observador de notificaciones en carga

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) Eliminar el Observador de notificaciones como

deinit {
        NotificationCenter.default.removeObserver(self)
    }

3) Agregue métodos de mostrar / ocultar teclado como

 @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) Agregue delegado de textfeild y agregue toques Métodos comenzados .usefull para ocultar el teclado cuando toque fuera del textfeild en la pantalla

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

    }
pansora abhay
fuente
necesita serUIKeyboardFrameEndUserInfoKey
Micro
1
NSNotification.Name.UIKeyboardWillShow se renombra a UIResponder.keyboardWillShowNotificationtambién UIKeyboardFrameBeginUserInfoKey paraUIResponder.keyboardFrameBeginUserInfoKey
smj 01 de
7

Esta es una versión mejorada de @JosephLord y la respuesta de @ Hlung. Se puede aplicar si tienes tabbar o no. Y restauraría perfectamente la vista que el teclado mueve a su posición original.

// 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)
        }
}
Jeff Gu Kang
fuente
6

La forma más fácil que no requiere ningún código:

  1. Descargar KeyboardLayoutConstraint.swift y agregue (arrastre y suelte) el archivo en su proyecto, si aún no está utilizando el marco de animación Spring.
  2. En su guión gráfico, cree una restricción inferior para el objeto / vista / campo de texto, seleccione la restricción (haga doble clic en ella) y en el Inspector de identidad, cambie su clase de NSLayoutConstraint a KeyboardLayoutConstraint.
  3. ¡Hecho!

El objeto se moverá automáticamente hacia arriba con el teclado, en sincronización.

gammachill
fuente
2
Gran solución! Pero necesita el Área segura. Parte inferior como el primer elemento de la restricción (no funcionó para mí cuando era el segundo elemento). Y funcionó mejor con la constante establecida en 0, ya que conserva la constante y la ajusta, en lugar de simplemente moverla lo suficiente como para mostrar el campo y el teclado.
Mythlandia
¿Tiene la versión swift4 KeyboardLayoutConstraint?
jeet.chanchawat
6

Creé un protocolo Swift 3 para manejar la apariencia / desaparición del teclado

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

}

Luego, para implementarlo en un UIViewController, haga lo siguiente:

  • deje que viewController se ajuste a este protocolo:

    class FormMailVC: UIViewControlle, KeyboardHandler {
  • comience a observar los cambios de teclado en viewWillAppear:

    // MARK: - View controller life cycle
    override func viewWillAppear(_ animated: Bool) {
      super.viewWillAppear(animated)
      startObservingKeyboardChanges()
    }
  • deje de observar los cambios del teclado en viewWillDisappear:

    override func viewWillDisappear(_ animated: Bool) {
      super.viewWillDisappear(animated)
      stopObservingKeyboardChanges()
    }
  • cree un IBOutlet para la restricción inferior del guión gráfico:

    // NSLayoutConstraints
    @IBOutlet weak var bottomConstraint: NSLayoutConstraint!

    (Recomiendo tener toda su IU incrustada dentro de un "contentView", y vincular a esta propiedad la restricción inferior de este contentView a la guía de diseño inferior) Limitación inferior de la vista de contenido

  • cambie la prioridad de restricción de la restricción superior a 250 (baja)

Limitación superior de la vista de contenido

Esto es para permitir que toda la vista de contenido se deslice hacia arriba cuando aparece el teclado. La prioridad debe ser más baja que cualquier otra prioridad de restricción en las subvistas, incluidas las prioridades de abrazo de contenido / prioridades de resistencia a la compresión de contenido.

  • Asegúrese de que su Autolayout tenga suficientes restricciones para determinar cómo debe deslizarse hacia arriba el contentView.

Puede que tenga que agregar una restricción "mayor que igual" para esto: restricción "mayor que igual"

¡Y aquí tienes! Sin teclado

Con teclado

Frédéric Adda
fuente
Funciona si también pones "Relación = Igual", sin advertencias.
Valtoni Boaventura
Si pones una relación igual, puede funcionar solo en situaciones específicas. En otros, puede tener una advertencia de inconsistencia de diseño automático. Depende de tu propio diseño. Es por eso que dije "quizás tengas que hacerlo".
Frédéric Adda
Ok Frédéric, de acuerdo. Fue una buena solución!
Valtoni Boaventura
desaparecidoimport UIKit
Mirko
6

Tal UIViewController extensión simple puede ser utilizada

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

Ejemplo de uso

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

Solución 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)
  }
}

Swift 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")
                                                }
        })
    }
}
ale_stro
fuente
2
mucho más "rápido" que las otras soluciones / funciona muy bien / reutilizable en cada controlador sin reescribir todo -> definitivamente el mejor aquí :)
Tib
Lo intenté pero no hay posibilidad, ¿se necesita UIScrollView o qué?
erdemgc
@erdemgc viste Ejemplo de uso? Todo lo que necesitas es solo UIViewControlller + addKeyboardChangeFrameObserver y luego no olvides eliminarlo
ale_stro
El removeKeyboardObserver()método aquí en realidad no elimina al observador. Si no llama a esto, verá un Invalid conditions for UIKeyboardWillChangeFrameNotificationen la consola desde el método add. Si llama a esto, verá el mismo error, lo que significa que el observador no se elimina. La documentación dice "Para cancelar el registro de observaciones, pasa el objeto devuelto por este método a removeObserver(_:)". Entonces, lo que debe hacer es guardar el objeto devuelto por ese método y luego pasarlo cuando desee eliminar el observador.
Huy-Anh Hoang
En realidad, una vez que se carga la vista de desplazamiento, al límite se le asigna un valor y no puede detectar si un teclado estará oculto si el marco del teclado se cruza con el límite.
James Kim
5

Puede usar esta biblioteca y solo una línea de código en appDidFinishedLaunching y listo.

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

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

IQKeyboardManager: ajuste la vista siempre que aparezca el enlace del teclado: https://github.com/hackiftekhar/IQKeyboardManager

Abdul Karim
fuente
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()

}

Y por último, ya que estamos utilizando métodos de delegados

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

refactorizado a partir del objetivo-c http://www.cocoawithlove.com/2008/10/sliding-uitextfields-around-to-avoid.html

Solomon Ayoola
fuente
Esta solución funcionó para mí, aunque tuve que hacer un trabajo adicional: declarar un var animateDistance: CGFloat!plus que tenía que manejar la Notificación UIKeyboardWillHide para cuando el usuario presiona el botón Ocultar teclado.
Rhuantavan
4

Otra solución que no depende de la distribución automática, las restricciones o cualquier salida. Lo que necesita es su campo (s) en una vista de desplazamiento.

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

}
Simpa
fuente
1
Se muestra la pantalla en negro después de la UIKeyboardWillShowNotificationllamada.
Sachin Kumaram
4

Aquí está mi versión para una solución para Swift 2.2:

Primero regístrese para mostrar / ocultar notificaciones de teclado

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

Luego, en los métodos correspondientes a esas notificaciones, mueva la vista principal hacia arriba o hacia abajo

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
}

El truco está en la parte "keyboardWillShow", que recibe llamadas cada vez que la "Barra de sugerencias QuickType" se expande o colapsa. Luego, siempre establecemos la coordenada y de la vista principal, que es igual al valor negativo de la altura total del teclado (con o sin la parte "Barra de tipo rápido").

Al final no olvides eliminar a los observadores

deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Pavle Mijatovic
fuente
3

La siguiente es una solución simple, por la cual el campo de texto tiene una restricción que lo vincula a la guía de diseño inferior. Simplemente agrega la altura del teclado a la constante de la restricción.

// 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
    }
}
Echessa
fuente
3

Bueno, creo que podría ser demasiado tarde, pero encontré otra versión simple de la respuesta de Saqib. Estoy usando Autolayout con restricciones. Tengo una pequeña vista dentro de otra vista principal con campos de nombre de usuario y contraseña. En lugar de cambiar la coordenada y de la vista, estoy guardando el valor de restricción original en una variable y cambiando la constante de la restricción a algún valor y nuevamente después de que el teclado se cierra, estoy configurando la restricción a la original. De esta forma, evita el problema que tiene la respuesta de Saqib, (La vista sigue subiendo y no se detiene). Debajo está mi código ...

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
  }
Sashi
fuente
Dentro del método keyboardWillShow , verifique la condición si self.centerYConstraint.constant == self.originalCenterYConstraint tiene esa línea de código entre estas condiciones. OriginalCenterYContraint es el valor original de centerYContraint que estoy almacenando en viewdidload. Esto funcionó para mí.
Sashi
3

Respuesta rápida 4.x, combinando respuestas de @Joseph Lord y @Isuru. bottomConstraintrepresenta la restricción inferior de la vista que le interesa mover.

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)
    }
}
Crashalot
fuente
2

Lo he hecho de la siguiente manera:

Esto es útil cuando textview superview es view

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
    }
    } }
Krishna Gawade
fuente
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

    }
Kamalkumar.E
fuente
1
Use el código anterior para mover el campo de texto sobre el teclado en Swift 2.2, funcionará bien. Espero que ayude a alguien.
Kamalkumar
1

Lo he hecho de la siguiente manera:

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
Vinod Joshi
fuente
1

Si eres como yo y probé todas las soluciones anteriores y aún no se resuelve tu problema, tengo una excelente solución para ti que funciona de maravilla. Primero quiero aclarar algunas cosas sobre algunas de las soluciones mencionadas anteriormente.

  1. En mi caso, IQkeyboardmanager funcionaba solo cuando no se aplicaba un diseño automático a los elementos; si se aplica, IQkeyboard Manager no funcionará de la manera que pensamos.
  2. Lo mismo con el movimiento ascendente de la vista propia.
  3. He escrito un encabezado objetivo c con un soporte rápido para empujar UITexfield hacia arriba cuando el usuario hace clic en él, resolviendo el problema del teclado que cubre el UITextfield: https://github.com/coolvasanth/smart_keyboard .
  4. Quien tenga un nivel intermedio o superior en el desarrollo de aplicaciones iOS puede comprender fácilmente el repositorio e implementarlo. Todo lo mejor
Vasanth
fuente
1

Aquí hay una solución genérica para todos los pasos de TextField:

1) Cree un ViewController común que sea extendido por otros ViewControllers

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) Cree una extensión de UIViewController y el TextField actualmente activo

//get active text field

extensión 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
}

}

Mintu Borah
fuente
Por alguna razón, estaba teniendo un problema con la función keyboardWillShow, el tamaño del teclado obtenía un tamaño incorrecto después del primer alternar del teclado (el primer conmutador tiene el marco correcto). Lo arreglé cambiándolo para proteger let userInfo = notificación.userInfo else {return} proteger let keyboardSize = userInfo [UIResponder.keyboardFrameEndUserInfoKey] como? NSValue else {return} let keyboardFrame = keyboardSize.cgRectValue if self.view.frame.origin.y == 0 {self.view.frame.origin.y - = getMoveableDistance (keyboarHeight: keyboardFrame.height)}. Espero que esto ayude si alguien tiene el mismo problema :)
Youstanzr
1

Muy simple y sin necesidad de codificar más. Simplemente agregue pod 'IQKeyboardManagerSwift'su podfile y en su AppDelegatepágina agregue el código a continuación.

import IQKeyboardManagerSwift

y en didFinishLaunchingWithOptions()tipo de método

IQKeyboardManager.shared.enable = true

eso es. revise este enlace de video para una mejor comprensión https://youtu.be/eOM94K1ZWN8 Espero que esto lo ayude.

Raghib Arshi
fuente
¿Funciona para TextView y cómo puedo cambiar el título de la tecla de retorno "Listo"?
tdt kien
Ir a: - "IQKeyboardManager.m" Reemplace esta línea (Línea No. 968): - [textField addDoneOnKeyboardWithTarget: self action: @selector (doneAction :) shouldShowPlaceholder: _shouldShowTextFieldPlaceholder] con esto: - [textField addRightButtonOnKeyboardWithText: @ targetboard YOURTTT: @ Target target YOURTTX: @ target target YourTet: @ acción propia: @selector (doneAction :) shouldShowPlaceholder: _shouldShowTextFieldPlaceholder]; Y aún no he probado la vista de texto, espero que esto te ayude.
Raghib Arshi
1

Código completo para gestionar el teclado.

        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
            }
        }
Avijit Nagare
fuente
0

Modifiqué un poco la solución @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
        })
    }
}
Empalagar
fuente
0

Ninguno de ellos funcionó y terminé usando inserciones de contenido para mover mi vista hacia arriba cuando aparece el teclado.

Nota: Estaba usando un UITableView

Solución referenciada @ keyboard-content-offset que fue escrita completamente en el objetivo C, la solución a continuación es limpia Swift.

Agregue el observador de notificaciones @ 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);

Para obtener el tamaño del teclado, primero obtenemos el diccionario userInfo del objeto de notificación, que almacena cualquier objeto adicional que nuestro receptor pueda usar.

De ese diccionario podemos obtener el objeto CGRect que describe el marco del teclado usando la tecla UIKeyboardFrameBeginUserInfoKey.

Aplique el contenido para el método de vista de tabla @ 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;
    }
}

Restaurar el método view @ keyboardWillBeHidden

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

    yourTableView.contentInset = UIEdgeInsetsZero;

    yourTableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}

Si desea mantener la orientación del dispositivo también en consideración, use declaraciones condicionales para adaptar el código a sus necesidades.

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

// Landscape
UIEdgeInsetsMake(0.0, 0.0, (keyboardSize.width), 0.0);
Vikram Ezhil
fuente
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)
    }
}

ingrese la descripción de la imagen aquí

Loco
fuente
0

La solución Swift 4 que uso toma el tamaño del teclado. Reemplace serverStatusStackViewcon cualquier vista que le interese, por ejemplo 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)
}
A
fuente