Mueve una vista hacia arriba solo cuando el teclado cubre un campo de entrada

148

Estoy tratando de construir una pantalla de entrada para el iPhone. La pantalla tiene varios campos de entrada. La mayoría de ellos en la parte superior de la pantalla, pero dos campos están en la parte inferior. Cuando el usuario intenta editar el texto en la parte inferior de la pantalla, aparecerá el teclado y cubrirá la pantalla. Encontré una solución simple para mover la pantalla hacia arriba cuando esto sucede, pero el resultado es que la pantalla siempre se mueve hacia arriba y los campos en la parte superior de la pantalla se alejan cuando el usuario intenta editarlos.

¿Hay alguna manera de que la pantalla solo se mueva cuando se editan los campos inferiores?

He usado este código que encontré aquí :

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

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

func keyboardWillHide(sender: NSNotification) {
    self.view.frame.origin.y += 150
}
John Allijn
fuente
es posible que pueda usar func textFieldDidBeginEditing (textField: UITextField!) {} para detectar qué campo de texto ha comenzado a editar y ocultar / mostrar el teclado
humblePilgrim
Olvidé mencionar que soy nuevo en Swift :( ¿Cuál sería la sintaxis correcta para verificar esto? (¿Cómo obtengo el nombre del campo en esta función?)
John Allijn

Respuestas:

211

Su problema está bien explicado en este documento por Apple . El código de ejemplo en esta página (at Listing 4-1) hace exactamente lo que necesita, desplazará su vista solo cuando la edición actual esté debajo del teclado. Solo necesita poner los controles necesarios en una vista de desplazamiento. El único problema es que este es Objective-C y creo que lo necesitas en Swift ... así que aquí está:

Declarar una variable

var activeField: UITextField?

luego agregue estos métodos

 func registerForKeyboardNotifications()
{
    //Adding notifies on keyboard appearing
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWasShown:", name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: "keyboardWillBeHidden:", name: UIKeyboardWillHideNotification, object: nil)
}


func deregisterFromKeyboardNotifications()
{
    //Removing notifies on keyboard appearing
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}

func keyboardWasShown(notification: NSNotification)
{
    //Need to calculate keyboard exact size due to Apple suggestions
    self.scrollView.scrollEnabled = true
    var info : NSDictionary = notification.userInfo!
    var keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().size
    var contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height, 0.0)

    self.scrollView.contentInset = contentInsets
    self.scrollView.scrollIndicatorInsets = contentInsets

    var aRect : CGRect = self.view.frame
    aRect.size.height -= keyboardSize!.height
    if let activeFieldPresent = activeField
    {
        if (!CGRectContainsPoint(aRect, activeField!.frame.origin))
        {
            self.scrollView.scrollRectToVisible(activeField!.frame, animated: true)
        }
    }


}


func keyboardWillBeHidden(notification: NSNotification)
{
    //Once keyboard disappears, restore original positions
    var info : NSDictionary = notification.userInfo!
    var keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue().size
    var contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, -keyboardSize!.height, 0.0)
    self.scrollView.contentInset = contentInsets
    self.scrollView.scrollIndicatorInsets = contentInsets
    self.view.endEditing(true)
    self.scrollView.scrollEnabled = false

}

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

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

Asegúrese de declarar su ViewController como UITextFieldDelegatey establecer delegados correctos en sus métodos de inicialización: por ejemplo:

self.you_text_field.delegate = self

Y recuerde llamar registerForKeyboardNotificationsa viewInit y deregisterFromKeyboardNotificationsal salir.

Editar / Actualizar: sintaxis Swift 4.2

func registerForKeyboardNotifications(){
    //Adding notifies on keyboard appearing
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: NSNotification.Name.UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIResponder.keyboardWillHideNotification, object: nil)
}

func deregisterFromKeyboardNotifications(){
    //Removing notifies on keyboard appearing
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIResponder.keyboardWillHideNotification, object: nil)
}

@objc func keyboardWasShown(notification: NSNotification){
    //Need to calculate keyboard exact size due to Apple suggestions
    self.scrollView.isScrollEnabled = true
    var info = notification.userInfo!
    let keyboardSize = (info[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
    let contentInsets : UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize!.height, right: 0.0)

    self.scrollView.contentInset = contentInsets
    self.scrollView.scrollIndicatorInsets = contentInsets

    var aRect : CGRect = self.view.frame
    aRect.size.height -= keyboardSize!.height
    if let activeField = self.activeField {
        if (!aRect.contains(activeField.frame.origin)){
            self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
        }
    }
}

@objc func keyboardWillBeHidden(notification: NSNotification){
    //Once keyboard disappears, restore original positions
    var info = notification.userInfo!
    let keyboardSize = (info[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
    let contentInsets : UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: -keyboardSize!.height, right: 0.0)
    self.scrollView.contentInset = contentInsets
    self.scrollView.scrollIndicatorInsets = contentInsets
    self.view.endEditing(true)
    self.scrollView.isScrollEnabled = false
}

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

func textFieldDidEndEditing(_ textField: UITextField){
    activeField = nil
}
Nerkyator
fuente
3
Debe llamar a registerForKeyboardNotifications en viewDidLoad, por lo que agregará un observador al centro de notificaciones para cuando el teclado aparece o desaparece de la pantalla. Cuando se activen estas notificaciones, se invocarán los métodos keyboardWasShown y keyboardWillBeHidden y luego scrollview se moverá según el tamaño del teclado. Puede encontrar información más detallada sobre NotificationCenter aquí: developer.apple.com/library/mac/documentation/Cocoa/Reference/…
Nerkyator
12
Gracias, eso es exactamente lo que estaba buscando: una solución recomendada por Apple. Sin embargo, en mi caso ya tenía una vista de desplazamiento que se extendía fuera del área visible. Este código deshabilitaría el desplazamiento después de que el teclado se ocultara. Eliminé "self.scrollView.scrollEnabled = false" y todavía no se desplazaría. Lo que funcionó para mí fue "self.scrollView.contentInset = UIEdgeInsetsZero;" esta sola línea en el evento
KeyboardWillHide
55
Código perfecto Pero necesito usar en UIKeyboardFrameEndUserInfoKeylugar de UIKeyboardFrameBeginUserInfoKey, porque este último devuelve la altura 0. Y cambia el punto de prueba al fondo, no el origen. if let activeField = self.activeField { var point = activeField.frame.origin point.y += activeField.frame.size.height if (!aRect.contains(point)){ self.scrollView.scrollRectToVisible(activeField.frame, animated: true) } }
Victor Choy
3
@MaX: Debería funcionar, pero si desea una solución Swift4, puede consultar este ejemplo de iOS del libro de Matt Neuburg que sugiero a todos los desarrolladores de iOS :)
Nerkyator
1
@MaX No estoy seguro si esto es exclusivamente una cosa rápida de 4, pero lo que parece estar sucediendo es que el textField's didBeginEditingmétodo se llama DESPUÉS de que se llamó al keyboardWillShowmétodo. Como resultado, la activeFieldvariable sigue siendo nula, lo que significa que no ocurre desplazamiento automático. Mi solución fue poner la activeField = textFieldllamada en el shouldBeginEditingmétodo textField . Esto resuelve el problema del orden de las llamadas.
HirdayGupta
150

Aquí están mis 2 centavos:

¿Has probado: https://github.com/hackiftekhar/IQKeyboardManager

Extremadamente fácil de instalar Swift u Objective-C.

Aquí cómo funciona:

IQKeyboardManager (Swift): - IQKeyboardManagerSwift está disponible a través de CocoaPods, para instalarlo simplemente agregue la siguiente línea a su Podfile: (# 236)

pod 'IQKeyboardManagerSwift'

En AppDelegate.swift, solo importe el marco IQKeyboardManagerSwift y habilite IQKeyboardManager.

import IQKeyboardManagerSwift

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

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

    IQKeyboardManager.sharedManager().enable = true
    // For Swift 4, use this instead
    // IQKeyboardManager.shared.enable = true


    return true
    }
}

Y eso es todo. ¡Fácil!

Señor h
fuente
17
Perfecto. Esto debería estar integrado por defecto. Parece ridículo que no lo sea.
NathofGod
1
Esto es realmente ridículo, es tan fácil de implementar y simplemente funciona, además de obtener una manera fácil de alternar entre múltiples campos de texto
Khoury
alternar en múltiples campos de texto no funcionaría? no aparece ninguna flecha en el teclado para moverse entre los campos de texto.
Arsal
1
Esta fue una solución increíble, ¡gracias! Me llevó un poco de trabajo configurar correctamente los CocoaPods, ya que eran completamente nuevos para mí. Después de configurarlo, fue literalmente 2 líneas de código para implementar esto y funcionó perfectamente de inmediato. ¡Muchas gracias!
IcyBlueRose
55
Esto no funcionó para mí en Swift 4.1 IQKeyboardManager.sharedManager (). Enable = true Ha sido cambiado a IQKeyboardManager.shared.enable = true
Rmalmoe
20

El que encontré que funcionaba perfectamente para mí fue este:

func textFieldDidBeginEditing(textField: UITextField) {
    if textField == email || textField == password {
        animateViewMoving(true, moveValue: 100)
    }
}

func textFieldDidEndEditing(textField: UITextField) {
    if textField == email || textField == password {
        animateViewMoving(false, moveValue: 100)
    }
}

func animateViewMoving (up:Bool, moveValue :CGFloat){
    let movementDuration:NSTimeInterval = 0.3
    let movement:CGFloat = ( up ? -moveValue : moveValue)

    UIView.beginAnimations("animateView", context: nil)
    UIView.setAnimationBeginsFromCurrentState(true)
    UIView.setAnimationDuration(movementDuration)

    self.view.frame = CGRectOffset(self.view.frame, 0, movement)
    UIView.commitAnimations()
}

También puede cambiar los valores de altura. Elimine la "instrucción if" si desea usarla para todos los campos de texto.

Incluso puede usar esto para todos los controles que requieren la entrada del usuario como TextView.

Eduardo
fuente
55
Mover un marco UIView directamente no es una gran solución. Además, esto incluye valores codificados que son específicos de este caso de uso. Animaría a las personas a que no implementen una solución como esta y, en cambio, hagan algo más cercano a las mejores prácticas como se describe en la respuesta aceptada.
MobileVet
@MobileVet Entendido, pero este es el que funciona.
Tarvo Mäesepp
increíble lógica separada sin cambiar en nuestro código
Dilip Jangid
6

¿Hay alguna manera de que la pantalla solo se mueva cuando se editan los campos inferiores ?

Tuve un problema similar y encontré una solución bastante sencilla sin usar scrollView, y en su lugar usando if en los métodos keyboardWillShow / Hide.

func keyboardWillShow(notification: NSNotification) {
    if bottomText.editing{
        self.view.window?.frame.origin.y = -1 * getKeyboardHeight(notification)
    }
}

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

Esta fue una buena solución para mí porque solo tenía dos campos de texto.

Desplaza toda la vista hacia arriba: solo cuando se editan ciertos campos de texto (bottomText)

Desplaza toda la vista hacia abajo: solo cuando la vista no está en la ubicación original

villejacob
fuente
5

Solo use esta extensión para mover cualquier UIView cuando se presente el teclado.

extension UIView {
    func bindToKeyboard(){
        NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillChange(_:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
    }

    @objc func keyboardWillChange(_ notification: NSNotification){
        let duration = notification.userInfo![UIKeyboardAnimationDurationUserInfoKey] as! Double
        let curve = notification.userInfo![UIKeyboardAnimationCurveUserInfoKey] as! UInt
        let beginningFrame = (notification.userInfo![UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
        let endFrame = (notification.userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue

        let deltaY = endFrame.origin.y - beginningFrame.origin.y

        UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
            self.frame.origin.y += deltaY
        }, completion: nil)
    }
}

Luego, en su vista descarga, vincule su vista al teclado

UiView.bindToKeyboard()
Bilal Mustafa
fuente
Esto mueve la vista hacia arriba, incluso si no es necesaria y si el campo de texto está en la parte superior de la vista, subirá y no será visible. Esta no es una buena solución.
Shivam Pokhriyal el
4

¿Por qué no implementar esto en un UITableViewController en su lugar? El teclado no ocultará ningún campo de texto cuando se muestre.

RJiryes
fuente
Esta es realmente la forma más fácil y robusta. Y funciona incluso con controles que usan un InputView personalizado en lugar del teclado predeterminado (las notificaciones de UIKeyboard no funcionan para estos)
Radu Simionescu
1
Existe el problema de que debe tener cuidado si la vista de tabla está incrustada (en lugar de ser "toda la página"). Para cualquiera que haya tropezado con eso, la solución se explica en publicaciones como esta y esta . ¡Espero que ayude a alguien a lidiar con el "problema más estúpido de Apple"!
Fattie
3

Swift 4 (** actualizado ) con extensión **

  1. agregue los botones en un contenedor
  2. conecte la restricción inferior del contenedor con IBOutlet containerBtmConstrain
  3. inViewDidLoad

    self.containerDependOnKeyboardBottomConstrain = containerBtmConstrain
    self.watchForKeyboard() 
  4. agregue la siguiente extensión

    import UIKit
    
    private var xoAssociationKeyForBottomConstrainInVC: UInt8 = 0
    
    extension UIViewController {
    
        var containerDependOnKeyboardBottomConstrain :NSLayoutConstraint! {
            get {
                return objc_getAssociatedObject(self, &xoAssociationKeyForBottomConstrainInVC) as? NSLayoutConstraint
            }
            set(newValue) {
                objc_setAssociatedObject(self, &xoAssociationKeyForBottomConstrainInVC, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
            }
        }
    
        func watchForKeyboard() {
            NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWasShown(notification:)), name:UIResponder.keyboardWillShowNotification, object: nil);
            NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(notification:)), name:UIResponder.keyboardWillHideNotification, object: nil);
        }
    
        @objc func keyboardWasShown(notification: NSNotification) {
            let info = notification.userInfo!
            guard let keyboardFrame: CGRect = (info[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else {
              return
            }
    
            UIView.animate(withDuration: 0.3, animations: { () -> Void in
                self.containerDependOnKeyboardBottomConstrain.constant = -keyboardFrame.height
                self.view.layoutIfNeeded()
            })
        }
    
        @objc func keyboardWillHide(notification: NSNotification) {
            UIView.animate(withDuration: 0.3, animations: { () -> Void in
                self.containerDependOnKeyboardBottomConstrain.constant = 0
                self.view.layoutIfNeeded()
            })
        }
    }
iluvatar_GR
fuente
1
Gracias amigo. Se trata de la clave correcta. Estaba usando UIKeyboardFrameBeginUserInfoKey y ahora uso UIKeyboardFrameEndUserInfoKey lo maneja con gracia.
Felipe
2

Utilizo SwiftLint, que tuvo algunos problemas con el formato de la respuesta aceptada. Específicamente:

sin espacio antes de los dos puntos, sin fuerza de lanzamiento, prefiera UIEdgeInset (arriba: etc ... en lugar de UIEdgeInsetMake.

así que aquí están esas actualizaciones para Swift 3

func registerForKeyboardNotifications() {
    //Adding notifies on keyboard appearing
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

func deregisterFromKeyboardNotifications() {
    //Removing notifies on keyboard appearing
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil)
    NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

func keyboardWasShown(notification: NSNotification) {
    //Need to calculate keyboard exact size due to Apple suggestions
    scrollView?.isScrollEnabled = true
    var info = notification.userInfo!
    if let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size {
        let contentInsets: UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize.height, right: 0.0)

        scrollView?.contentInset = contentInsets
        scrollView?.scrollIndicatorInsets = contentInsets

        var aRect: CGRect = self.view.frame
        aRect.size.height -= keyboardSize.height
        if let activeField = self.activeField {
            if !aRect.contains(activeField.frame.origin) {
                self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
            }
        }
    }
}

func keyboardWillBeHidden(notification: NSNotification) {
    //Once keyboard disappears, restore original positions
    var info = notification.userInfo!
    if let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size {
        let contentInsets: UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: -keyboardSize.height, right: 0.0)
        scrollView?.contentInset = contentInsets
        scrollView?.scrollIndicatorInsets = contentInsets
    }

    view.endEditing(true)
    scrollView?.isScrollEnabled = false
}

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

func textFieldDidEndEditing(_ textField: UITextField) {
    activeField = nil
}
Joe Hoffman
fuente
1

Creo que esta cláusula está mal:

if (!CGRectContainsPoint(aRect, activeField!.frame.origin))

Si bien el origen de activeField puede estar por encima del teclado, el maxY podría no ...

Crearía un punto 'máximo' para activeField y comprobaría si está en el teclado Rect.

zevij
fuente
0

Aquí está mi versión después de leer la documentación proporcionada por Apple y las publicaciones anteriores. Una cosa que noté es que el textView no se manejó cuando estaba cubierto por el teclado. Desafortunadamente, la documentación de Apple no funcionará porque, por cualquier razón, el teclado se llama DESPUÉS de que se llama a textViewDidBeginEditing. Manejé esto llamando a un método central que verifica si se muestra el teclado Y si se está editando un textView o textField. De esta manera, el proceso solo se activa cuando AMBAS condiciones existen.

Otro punto con textViews es que su altura puede ser tal que el teclado recorta la parte inferior de textView y no se ajustaría si el punto superior izquierdo de la vista estuviera a la vista. Entonces, el código que escribí en realidad toma el punto Inferior-Izquierdo referenciado por la pantalla de cualquier textView o textField y ve si cae en las coordenadas referenciadas en la pantalla del teclado presentado, lo que implica que el teclado cubre una parte de él.

let aRect : CGRect = scrollView.convertRect(activeFieldRect!, toView: nil)
    if (CGRectContainsPoint(keyboardRect!, CGPointMake(aRect.origin.x, aRect.maxY))) {
        // scroll textView/textField into view
    }

Si está utilizando un controlador de navegación, la subclase también establece el ajuste automático de la vista de desplazamiento para las inserciones en falso.

self.automaticallyAdjustsScrollViewInsets = false

Recorre cada textView y textField para establecer delegados para el manejo

    for view in self.view.subviews {
        if view is UITextView {
            let tv = view as! UITextView
            tv.delegate = self
        } else if view is UITextField {
            let tf = view as! UITextField
            tf.delegate = self
        }
    }

Simplemente configure su clase base en la subclase creada aquí para obtener resultados.

import UIKit

class ScrollingFormViewController: UIViewController, UITextViewDelegate, UITextFieldDelegate {

var activeFieldRect: CGRect?
var keyboardRect: CGRect?
var scrollView: UIScrollView!

override func viewDidLoad() {

    self.automaticallyAdjustsScrollViewInsets = false

    super.viewDidLoad()

    // Do any additional setup after loading the view.
    self.registerForKeyboardNotifications()
    for view in self.view.subviews {
        if view is UITextView {
            let tv = view as! UITextView
            tv.delegate = self
        } else if view is UITextField {
            let tf = view as! UITextField
            tf.delegate = self
        }
    }
    scrollView = UIScrollView(frame: self.view.frame)
    scrollView.scrollEnabled = false
    scrollView.showsVerticalScrollIndicator = false
    scrollView.showsHorizontalScrollIndicator = false
    scrollView.addSubview(self.view)
    self.view = scrollView
}

override func viewDidLayoutSubviews() {
    scrollView.sizeToFit()
    scrollView.contentSize = scrollView.frame.size
    super.viewDidLayoutSubviews()
}

deinit {
    self.deregisterFromKeyboardNotifications()
}

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


func registerForKeyboardNotifications()
{
    //Adding notifies on keyboard appearing
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ScrollingFormViewController.keyboardWasShown), name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ScrollingFormViewController.keyboardWillBeHidden), name: UIKeyboardWillHideNotification, object: nil)
}


func deregisterFromKeyboardNotifications()
{
    //Removing notifies on keyboard appearing
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillShowNotification, object: nil)
    NSNotificationCenter.defaultCenter().removeObserver(self, name: UIKeyboardWillHideNotification, object: nil)
}

func keyboardWasShown(notification: NSNotification)
{
    let info : NSDictionary = notification.userInfo!
    keyboardRect = (info[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.CGRectValue()
    adjustForKeyboard()
}


func keyboardWillBeHidden(notification: NSNotification)
{
    keyboardRect = nil
    adjustForKeyboard()
}

func adjustForKeyboard() {
    if keyboardRect != nil && activeFieldRect != nil {
        let aRect : CGRect = scrollView.convertRect(activeFieldRect!, toView: nil)
        if (CGRectContainsPoint(keyboardRect!, CGPointMake(aRect.origin.x, aRect.maxY)))
        {
            scrollView.scrollEnabled = true
            let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardRect!.size.height, 0.0)
            scrollView.contentInset = contentInsets
            scrollView.scrollIndicatorInsets = contentInsets
            scrollView.scrollRectToVisible(activeFieldRect!, animated: true)
        }
    } else {
        let contentInsets : UIEdgeInsets = UIEdgeInsetsZero
        scrollView.contentInset = contentInsets
        scrollView.scrollIndicatorInsets = contentInsets
        scrollView.scrollEnabled = false
    }
}

func textViewDidBeginEditing(textView: UITextView) {
    activeFieldRect = textView.frame
    adjustForKeyboard()
}

func textViewDidEndEditing(textView: UITextView) {
    activeFieldRect = nil
    adjustForKeyboard()
}

func textFieldDidBeginEditing(textField: UITextField)
{
    activeFieldRect = textField.frame
    adjustForKeyboard()
}

func textFieldDidEndEditing(textField: UITextField)
{
    activeFieldRect = nil
    adjustForKeyboard()
}

}
usuario2690492
fuente
0

Ya se dan respuestas impresionantes, pero esta es una forma diferente de lidiar con esta situación (usando Swift 3x ):

En primer lugar, llame al siguiente método en viewWillAppear()

func registerForKeyboardNotifications() {

NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWasShown), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillBeHidden), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

}

Ahora tome una IBOutletde UIViewlas principales restricciones de su UIViewcontrollerestilo de esta manera: (aquí UIViewestá la subvista de UIScrollVieweso significa que debe tener un UIScrollViewpara todos sus subViews)

@IBOutlet weak var loginViewTopConstraint: NSLayoutConstraint!

Y otra variable como seguir y agregar un delegado, es decir UITextFieldDelegate:

var activeTextField = UITextField() //This is to keep the reference of UITextField currently active

Después de eso, aquí está la parte mágica, solo pegue este fragmento a continuación :

func keyboardWasShown(_ notification: Notification) {

let keyboardInfo  = notification.userInfo as NSDictionary?

//print(keyboardInfo!)

let keyboardFrameEnd: NSValue? = (keyboardInfo?.value(forKey: UIKeyboardFrameEndUserInfoKey) as? NSValue)

let keyboardFrameEndRect: CGRect? = keyboardFrameEnd?.cgRectValue


if activeTextField.frame.origin.y + activeTextField.frame.size.height + 10 > (keyboardFrameEndRect?.origin.y)! {

    UIView.animate(withDuration: 0.3, delay: 0, options: .transitionFlipFromTop, animations: {() -> Void in

        //code with animation

        //Print some stuff to know what is actually happening
        //print(self.activeTextField.frame.origin.y)
        //print(self.activeTextField.frame.size.height)
        //print(self.activeTextField.frame.size.height)

        self.loginViewTopConstraint.constant = -(self.activeTextField.frame.origin.y + self.activeTextField.frame.size.height - (keyboardFrameEndRect?.origin.y)!) - 30.0

        self.view.layoutIfNeeded()

    }, completion: {(_ finished: Bool) -> Void in
        //code for completion

    })
}
}

func keyboardWillBeHidden(_ notification: Notification) {

UIView.animate(withDuration: 0.3, animations: {() -> Void in

    self.loginViewTopConstraint.constant = self.view.frame.origin.y
    self.view.layoutIfNeeded()

})
}

//MARK: textfield delegates
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
    activeTextField = textField
    return true
}

func textFieldShouldReturn(_ textField: UITextField) -> Bool {

           switch textField {
    case YOUR_TEXTFIELD_ONE:
        YOUR_TEXTFIELD_TWO.becomeFirstResponder()
        break
    case YOUR_TEXTFIELD_TWO:
        YOUR_TEXTFIELD_THREE.becomeFirstResponder()
        break
    default:
        textField.resignFirstResponder()
        break
    }
    return true
}

Ahora el último fragmento:

//Remove Keyboard Observers
override func viewWillDisappear(_ animated: Bool) {

NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardDidShow, object: nil)

NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}

No olvide asignar delegados a todos sus UITextFieldcorreos electrónicos.UIStoryboard

¡Buena suerte!

Anurag Sharma
fuente
0

Sintaxis de Swift 3:

func textFieldDidBeginEditing(_ textField: UITextField) {
    // add if for some desired textfields
        animateViewMoving(up: true, moveValue: 100)
}

func textFieldDidEndEditing(_ textField: UITextField) {
    // add if for some desired textfields
        animateViewMoving(up: false, moveValue: 100)
}

func animateViewMoving (up:Bool, moveValue :CGFloat){
     textFieldDidEndEditing(_ textField: UITextField) {

    let movementDuration:TimeInterval = 0.5

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

este es un buen método para obtener lo que desea, puede agregar condiciones "si" para ciertos campos de texto, pero este tipo funciona para todos ... Espero que pueda ser útil para todos

Atrin Noori
fuente
0

En primer lugar, declare una variable para identificar su UITextField activo.

Paso 1:-

Me gusta como var activeTextField: UITextField?

Paso 2: - Después de esto, agregue estas dos líneas en viewDidLoad.

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)

Paso 3:-

Ahora defina estos dos métodos en su clase de controlador.

func keyboardWillShow(_ notification: NSNotification) {

    self.scrollView.isScrollEnabled = true
    var info = notification.userInfo!
    let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
    let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize!.height, 0.0)

    self.scrollView.contentInset = contentInsets
    self.scrollView.scrollIndicatorInsets = contentInsets

    var aRect : CGRect = self.view.frame
    aRect.size.height -= keyboardSize!.height
    if let activeField = self.activeField {
        if (!aRect.contains(activeField.frame.origin)){
            self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
        }
    }
}


func keyboardWillHide(_ notification: NSNotification) {

    let contentInsets : UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0)
    self.scrollView.contentInset = contentInsets
    self.scrollView.scrollIndicatorInsets = contentInsets
    self.view.endEditing(true)
    self.scrollView.isScrollEnabled = true
}


func textFieldDidBeginEditing(_ textField: UITextField){

    activeField = textField
}

func textFieldDidEndEditing(_ textField: UITextField){

    activeField = nil
}
Hemendra Singh
fuente
0

para rápido 4.2.

Esto se aplicará a cualquier forma. No hay necesidad de scrollview. no te olvides de establecer delegado.

Hacer una var de uitextfield

var clickedTextField = UITextField()

En su vista, cargamos

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

Conozca el campo de texto cliqueado. probablemente tenga campos de texto en toda la pantalla.

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

Compruebe si el teclado está cubriendo el campo de texto o no.

@objc func keyboardWillShow(sender: NSNotification,_ textField : UITextField) {
    if let keyboardSize = (sender.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {

        if clickedTextField.frame.origin.y > keyboardSize.origin.y {
            self.view.frame.origin.y = keyboardSize.origin.y - clickedTextField.center.y - 20
        }
    }
}

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

Regresar al teclado cerrado

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

ACTUALIZACIÓN: NSNotification.Name.UIKeyboardWillShow y NSNotification.Name.UIKeyboardWillHide se renombran a UIResponder.keyboardWillShowNotification y UIResponder.keyboardWillHideNotification respectivamente.

rupesh45
fuente
0

Swift : puede hacer esto comprobando qué textField se presenta.

@objc func keyboardWillShow(notification: NSNotification) {
    if self.textField.isFirstResponder == true {
        self.view.frame.origin.y -= 150
     }
}

@objc func keyboardWillHide(notification: NSNotification){
    if self.textField.isFirstResponder == true {
       self.view.frame.origin.y += 150
    }
}
ZAFAR007
fuente
-1

Este código sube el campo de texto que está editando para que pueda verlo en Swift 3 para esta respuesta, también debe hacer que su vista sea un UITextFieldDelegate:

var moveValue: CGFloat!
var moved: Bool = false
var activeTextField = UITextField()

func textFieldDidBeginEditing(_ textField: UITextField) {
    self.activeTextField = textField
}
func textFieldDidEndEditing(_ textField: UITextField) {
    if moved == true{
    self.animateViewMoving(up: false, moveValue: moveValue )
        moved = false
    }
}
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()
}

Y luego en viewDidLoad:

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

Qué llamadas (fuera de viewDidLoad):

func keyboardWillShow(notification: Notification) {
    if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
        let keyboardHeight = keyboardSize.height
        if (view.frame.size.height-self.activeTextField.frame.origin.y) - self.activeTextField.frame.size.height < keyboardHeight{
            moveValue = keyboardHeight - ((view.frame.size.height-self.activeTextField.frame.origin.y) - self.activeTextField.frame.size.height)
            self.animateViewMoving(up: true, moveValue: moveValue )
            moved = true
        }
    }
}
joshLor
fuente
-1

Swift 3

@IBOutlet var scrollView: UIScrollView!
@IBOutlet var edtEmail: UITextField!
@IBOutlet var bottomTextfieldConstrain: NSLayoutConstraint! // <- this guy is the constrain that connect the bottom of textField to lower object or bottom of page!

 @IBAction func edtEmailEditingDidBegin(_ sender: Any) { 
        self.bottomTextfieldConstrain.constant = 200
        let point = CGPoint(x: 0, y: 200)
        scrollView.contentOffset = point
    }

@IBAction func edtEmailEditingDidEnd(_ sender: Any) { 
    self.bottomTextfieldConstrain.constant = 50
}
Alfi
fuente
-1

La respuesta aceptada es casi perfecta. Pero necesito usar en UIKeyboardFrameEndUserInfoKeylugar de UIKeyboardFrameBeginUserInfoKey,porque este último devuelve la altura 0 del keyborad. Y cambia el punto de prueba al fondo, no el origen.

    var aRect : CGRect = self.view.frame
    aRect.size.height -= keyboardSize!.height
    if let activeField = self.activeField {
        var point = activeField.frame.origin
        point.y += activeField.frame.size.height
        if (!aRect.contains(point)){
            self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
        }
    }
Victor Choy
fuente
-1

Swift 4 actualizó mi solución

con animación de restricción en el teclado show / hide, disfruta.

import Foundation
import UIKit

class PhoneController: UIViewController, UITextFieldDelegate{

    var phoneLayoutYConstraint: NSLayoutConstraint?

    override func viewDidLoad() {

        super.viewDidLoad()

        view.backgroundColor = .white

        NotificationCenter.default.addObserver(self, selector: #selector(handleKeyBoardNotification(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(handleKeyBoardNotification(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
        phoneField.delegate = self

        view.addSubview(phoneField)

        NSLayoutConstraint.activate([phoneField.heightAnchor.constraint(equalToConstant: 50),
                                     phoneField.centerXAnchor.constraint(equalTo: view.centerXAnchor),
                                     phoneField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
                                     phoneField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)])

        phoneLayoutYConstraint = NSLayoutConstraint(item: phoneField, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
        phoneLayoutYConstraint?.isActive = true

    }

    let phoneField: UITextField = {
        let text = UITextField()
        text.translatesAutoresizingMaskIntoConstraints = false
        text.keyboardType = .numberPad
        text.font = UIFont.systemFont(ofSize: 30)
        text.layer.cornerRadius = 5.0
        text.layer.masksToBounds = true
        text.layer.borderColor = UIColor.darkGray.cgColor
        text.layer.borderWidth = 2.0

        return text
    }()


    override func viewDidDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        NotificationCenter.default.removeObserver(self)
    }

    func textFieldDidBeginEditing(_ textField: UITextField) {

    }


    func textFieldDidEndEditing(_ textField: UITextField) {

    }

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


   @objc func handleKeyBoardNotification(_ notification: NSNotification) {

        if let info = notification.userInfo {

            let keyboardSize = (info[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
            let isKeyBoardShowing = notification.name == NSNotification.Name.UIKeyboardWillShow

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


            phoneLayoutYConstraint?.constant = isKeyBoardShowing ? -keyboardSize!.height : 0

            UIView.animate(withDuration: 0, delay: 0, options: .curveEaseOut, animations: {
                self.view.layoutIfNeeded()
            }, completion: { (boo) in

            })

        }
    }

}
Hattori Hanzō
fuente
-1

Swift 4

Puede moverse fácilmente hacia arriba y hacia abajo UITextFieldcon el teclado con animación

ingrese la descripción de la imagen aquí

import UIKit

class ViewController: UIViewController {

    @IBOutlet var textField: UITextField!

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

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

    @objc func keyboardWillChange(notification: NSNotification) {

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

        UIView.animateKeyframes(withDuration: duration, delay: 0.0, options: UIViewKeyframeAnimationOptions(rawValue: curve), animations: {
            self.textField.frame.origin.y+=deltaY

        },completion: nil)
    }
ZAFAR007
fuente
Una vez más, una solución que empujará la vista hacia arriba innecesariamente incluso cuando no sea necesaria. Mejore su respuesta, es engañoso
Shivam Pokhriyal
-1

Swift 4.2

Mi solución centrará (verticalmente) la vista en UITextFieldsi su posición está debajo del teclado.

Paso 1: Cree un nuevo archivo rápido y copie y pegue la UIViewWithKeyboardclase.
Paso 2: en Interface Builder configúrelo como una Clase personalizada para su máximo rendimiento UIView.

import UIKit

class UIViewWithKeyboard: UIView {
    @IBInspectable var offsetMultiplier: CGFloat = 0.75
    private var keyboardHeight = 0 as CGFloat
    private weak var activeTextField: UITextField?
    override func awakeFromNib() {
        super.awakeFromNib()
        NotificationCenter.default.addObserver(self, selector: #selector(UIViewWithKeyboard.textDidBeginEditing),
                                               name: UITextField.textDidBeginEditingNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(UIViewWithKeyboard.keyboardWillShow),
                                               name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(UIViewWithKeyboard.keyboardWillHide),
                                               name: UIResponder.keyboardWillHideNotification, object: nil)
    }

    @objc func textDidBeginEditing(_ notification: NSNotification) {
        self.activeTextField = notification.object as? UITextField
    }

    @objc func keyboardWillShow(_ notification: Notification) {
        if let frameValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
            keyboardHeight = frameValue.cgRectValue.size.height
            if let textField = self.activeTextField {
                let offset = textField.frame.maxY < frame.maxY - keyboardHeight ? 0
                           : textField.frame.maxY - (frame.maxY - keyboardHeight) * offsetMultiplier
                self.setView(offset: offset)
            }
        }
    }

    @objc func keyboardWillHide(_ notification: NSNotification) {
        self.setView(offset: 0)
    }

    func setView(offset: CGFloat) {
        UIView.animate(withDuration: 0.25) {
            self.bounds.origin.y = offset
        }
    }
}
alcance
fuente
-1

Reescrito para swift 4.2

En ViewDidLoad ..

 NotificationCenter.default.addObserver(self, selector: #selector(trailViewController.keyboardWasShown), name: UIResponder.keyboardWillShowNotification, object: nil)
 NotificationCenter.default.addObserver(self, selector: #selector(trailViewController.keyboardWillBeHidden), name: UIResponder.keyboardWillHideNotification, object: nil)

Funciones restantes

func registerForKeyboardNotifications(){
    //Adding notifies on keyboard appearing
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWasShown(notification:)), name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillBeHidden(notification:)), name: UIResponder.keyboardWillHideNotification, object: nil)
}

func deregisterFromKeyboardNotifications(){
    //Removing notifies on keyboard appearing
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillShowNotification, object: nil)
    NotificationCenter.default.removeObserver(self, name: UIResponder.keyboardWillHideNotification, object: nil)
}

@objc func keyboardWasShown(notification: NSNotification){
    //Need to calculate keyboard exact size due to Apple suggestions
    self.scrollView.isScrollEnabled = true
    var info = notification.userInfo!
    let keyboardSize = (info[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
    let contentInsets : UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize!.height, right: 0.0)

    self.scrollView.contentInset = contentInsets
    self.scrollView.scrollIndicatorInsets = contentInsets

    var aRect : CGRect = self.view.frame
    aRect.size.height -= keyboardSize!.height
    if let activeField = self.activeField {
        if (!aRect.contains(activeField.frame.origin)){
            self.scrollView.scrollRectToVisible(activeField.frame, animated: true)
        }
    }
}

@objc func keyboardWillBeHidden(notification: NSNotification){
    //Once keyboard disappears, restore original positions
    var info = notification.userInfo!
    let keyboardSize = (info[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue.size
    let contentInsets : UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: -keyboardSize!.height, right: 0.0)
    self.scrollView.contentInset = contentInsets
    self.scrollView.scrollIndicatorInsets = contentInsets
    self.view.endEditing(true)
    self.scrollView.isScrollEnabled = false
}

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

func textFieldDidEndEditing(_ textField: UITextField){
    activeField = nil
}
VarunRaj
fuente
-2

"Olvidé mencionar que soy nuevo en Swift :( ¿Cuál sería la sintaxis correcta para verificar esto? (¿Cómo obtengo el nombre del campo en esta función?)"

Okay . Primero confirme con el protocolo UITextFieldDelegate

class YourClass:UITextFieldDelegate

Luego implemente la función

func textFieldDidBeginEditing(textField: UITextField!) {

    if textField == txtOne
    {
        println("TextOne")
    }
    if textField == txtTwo
    {
        println("TextTwo")
    }
}

Debe tener en cuenta que el enfoque adecuado es usar una vista de desplazamiento y colocar la vista que debe desplazarse hacia arriba / abajo dentro de la vista de desplazamiento y manejar los eventos del teclado en consecuencia

humilde peregrino
fuente
-3

Para Swift 4.2

Este código le permitirá controlar el momento del eje Y del marco para un tamaño de pantalla de dispositivo específico.

PD: este código no moverá de forma inteligente el marco según la ubicación de TextField.

Crear una extensión para UIDevice

extension UIDevice {
    enum ScreenType: String {
        case iPhone4_4S = "iPhone 4 or iPhone 4s"
        case iPhones_5_5s_5c_SE = "iPhone 5, iPhone 5s, iPhone 5c or iPhone SE"
        case iPhones_6_6s_7_8 = "iPhone 6, iPhone 6s, iPhone 7 or iPhone 8"
        case iPhones_6Plus_6sPlus_7Plus_8Plus = "iPhone 6 Plus, iPhone 6s Plus, iPhone 7 Plus or iPhone 8 Plus"
        case iPhoneX_Xs = "iPhone X, iPhone Xs"
        case iPhoneXR = "iPhone XR"
        case iPhoneXSMax = "iPhone Xs Max"
        case unknown
    }
    var screenType: ScreenType {
        switch UIScreen.main.nativeBounds.height {
        case 960:
            return .iPhone4_4S
        case 1136:
            return .iPhones_5_5s_5c_SE
        case 1334:
            return .iPhones_6_6s_7_8
        case 1920, 2208:
            return .iPhones_6Plus_6sPlus_7Plus_8Plus
        case 1792:
            return .iPhoneXR
        case 2436:
            return .iPhoneX_Xs
        case 2688:
            return .iPhoneXSMax
        default:
            return .unknown
        }
    }
}

Agregar NotificationObserver en 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)

Selector

@objc func keyboardWillShow(notification: NSNotification) {
    if ((notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue) != nil {
        if self.view.frame.origin.y == 0 {
            switch (UIDevice.current.screenType.rawValue) {
            case (UIDevice.ScreenType.iPhones_5_5s_5c_SE.rawValue):
                self.view.frame.origin.y -= 210
            case (UIDevice.ScreenType.iPhones_6_6s_7_8.rawValue):
                self.view.frame.origin.y -= 110
            case (UIDevice.ScreenType.iPhones_6Plus_6sPlus_7Plus_8Plus.rawValue):
                self.view.frame.origin.y -= 80
            case (UIDevice.ScreenType.iPhoneX_Xs.rawValue):
                self.view.frame.origin.y -= 70
            case (UIDevice.ScreenType.iPhoneXR.rawValue):
                self.view.frame.origin.y -= 70
            case (UIDevice.ScreenType.iPhoneXSMax.rawValue):
                self.view.frame.origin.y -= 70
            default:
                self.view.frame.origin.y -= 150
            }
        }
    }
}

@objc func keyboardWillHide(notification: NSNotification) {
    if ((notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue) != nil {
        if self.view.frame.origin.y != 0 {
            switch (UIDevice.current.screenType.rawValue) {
            case (UIDevice.ScreenType.iPhones_5_5s_5c_SE.rawValue):
                self.view.frame.origin.y += 210
            case (UIDevice.ScreenType.iPhones_6_6s_7_8.rawValue):
                self.view.frame.origin.y += 110
            case (UIDevice.ScreenType.iPhones_6Plus_6sPlus_7Plus_8Plus.rawValue):
                self.view.frame.origin.y += 80
            case (UIDevice.ScreenType.iPhoneX_Xs.rawValue):
                self.view.frame.origin.y += 70
            case (UIDevice.ScreenType.iPhoneXR.rawValue):
                self.view.frame.origin.y += 70
            case (UIDevice.ScreenType.iPhoneXSMax.rawValue):
                self.view.frame.origin.y += 70
            default:
                self.view.frame.origin.y += 150
            }
        }
    }
}
Kavin Varnan
fuente
1
En general, diría que es un enfoque malo y bastante costoso usar tipos específicos de dispositivos y dimensiones relacionadas con el diseño del código duro a medida que siguen cambiando.
Johan