Mover campo de texto quando o teclado aparecer rápido

217

Estou usando o Swift para programar com iOS e estou usando esse código para mover o UITextField, mas ele não funciona. Chamo a função keyboardWillShowcorretamente, mas o campo de texto não se move. Estou usando o 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
fonte
2
Guia passo a passo com arquivos de projeto: codebeaulieu.com/43/…
Dan Beaulieu
Talvez deinit e viewDidLoad não estejam equilibrados.
Ricardo
Com base nos documentos e na experiência pessoal da Apple. Aqui está meu repositório
Codetard

Respostas:

315

Há algumas melhorias a serem feitas nas respostas existentes.

Em primeiro lugar, o UIKeyboardWillChangeFrameNotification é provavelmente a melhor notificação, pois lida com alterações que não são apenas mostrar / ocultar, mas também devido a alterações no teclado (idioma, usando teclados de terceiros etc.) e rotações também (mas observe o comentário abaixo indicando que o teclado ocultará também ser tratado para suportar a conexão do teclado de hardware).

Em segundo lugar, os parâmetros de animação podem ser extraídos da notificação para garantir que as animações estejam corretamente juntas.

Provavelmente, existem opções para limpar esse código um pouco mais, especialmente se você se sentir confortável com a força de desembrulhar o código do dicionário.

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 explicar a animação do teclado fora da tela em vez de encolher, conforme o incrível comentário de @ Gabox abaixo)

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
fonte
1
@JosephLord nice. mas descobri que isso não funciona quando o teclado está oculto porque endFrame?.size.heightnão é nulo. Eu tenho o quadro final como UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 1024}, {768, 264}}";. Executado no iOS 8.3 iPad Simulator, Portrait. Xcode6.3 beta4.
Hlung
8
se o teclado não ocultar, tente usar esse código se endFrame? .origin.y> = UIScreen.mainScreen (). bounds.size.height {self.keyboardHeightLayoutConstraint? .constant = 0.0} else {self.keyboardHeightLayoutConstraint? .constant = endFrame.size.height}
Gabriel Goncalves
3
keyBoardHeightLayoutConstraint é uma restrição definida no InterfaceBuilder que restringe a parte inferior da visualização que você deseja mover / reduzir para o guia de layout inferior ou a parte inferior da visualização principal do controlador de visualização. A constante é inicialmente definida como zero e será ajustada para abrir espaço para o teclado quando o teclado aparecer ou mudar de tamanho.
Joseph Lord
2
Observe que .UIKeyboardWillChangeFramenão dispara quando um teclado de hardware está conectado, mesmo que o teclado do iOS desapareça. Você precisa observar .UIKeyboardWillHidetambém para entender esse caso extremo.
jamesk
1
@ Sulthan funciona bem .. Meu problema é que está ficando um pouco maior do que o teclado. existe alguma maneira de consertar isso?
Pavlos
128

Se você estiver usando o Layout Automático, presumo que você tenha definido o Espaço Inferior para a restrição Superview . Se for esse o caso, você simplesmente precisa atualizar o valor da restrição. Veja como você faz isso com um pouco de animação.

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

Os 20 codificados permanentemente são adicionados apenas para exibir um pouco o campo de texto acima do teclado. Caso contrário, a margem superior do teclado e a margem inferior do campo de texto estariam tocando.

Quando o teclado for descartado, redefina o valor da restrição para o original.

Isuru
fonte
1
Você pode me explicar como eu o defino? Obrigado! i sempre controlar tudo no storyboard
Pedro Manfredi
4
bottomConstraint é o nome que dei à restrição. Selecionei o constrante, arrastei e criei um IBOutlet para ele e dei esse nome. Você pode criar IBOutlets para restrições, assim como faria com outros elementos da interface do usuário, como botões e campos de texto.
Isuru
2
Essa resposta funcionou muito bem, exceto que a animação aconteceu imediatamente para mim. Marque Como animar alterações de restrição? para saber como animar corretamente.
31415 Adam
2
@ vinbhai4u Você precisa se registrar para receber uma UIKeyboardWillShowNotificationnotificação. Veja o código na pergunta do OP.
Isuru 30/05
8
@AdamJohns Para animar a alteração de restrição, atualize a constante fora animateWithDuratione chame self.view.layoutIfNeeded()dentro do bloco animado.
Max
110

Uma solução simples é mover a visualização para cima com a constante da altura do 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
fonte
4
Eu gosto desta solução simples. Mas eu adicionei um keyboardShowing boolean desde que movi mais de um campo de texto. Eu apenas movo o teclado uma vez enquanto ele está sendo exibido. Obrigado.
18715 Ken
2
em vez de mover a vista, mova o textView
ericgu
1
Isso manterá menos o valor y da visualização se o usuário alternar o idioma de entrada.
Jeffrey Neo
em vez de alterar o valor self.view, fiz o valor self.myConstraint, mas funciona, mas o ponto de vista (ao qual a restrição é aplicada) continua subindo. alguém enfrentou esse problema?
Sashi
5
Em vez de usar self.view.frame.origin.y -= 150use self.view.frame.origin.y = -150e em vez de self.view.frame.origin.y += 150usar self.view.frame.origin.y = 0. Isso evita que a visualização se mova 150 toda vez que um novo campo é tocado.
gunwin
43

Para mover sua visualização durante a edição do campo de texto, tente isso, apliquei isso,

Opção 1: - ** ** Atualização no Swift 5.0 e iPhone X, XR, XS e XS Max Move usando o NotificationCenter

  • Registre esta notificação em func viewWillAppear(_ animated: Bool)

  • Cancele o registro desta notificação em func viewWillDisappear(_ animated: Bool)

Nota: - Se você não fizer o cancelamento do registro, ele chamará da classe filho e causará uma falha ou outra falha.

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

Opção 2 :- Seu trabalho é bom

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

Eu recebi esta resposta desta fonte UITextField sobe quando o teclado aparece no Swift

No 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
fonte
1
@ Jogendra.Com, Obrigado por seu trabalho duro. Mas, funciona melhor no iPhone 5,4s e 6.Mas, como posso desabilitá-lo no iPhone 6plus e iPad (superiores)
Thiha Aung
Se você estiver usando a Opção 1, adicione um IBOutlet de restrição. Você criará uma restrição que deseja redimensionar usando o Layout automático e, em seguida, arraste e solte-a no viewcontroller para criar um IBOutlet que eu a refiro como self.iboutletConstraint.constant na função animar. Além disso, isso não reajusta a tomada ao ocultar o teclado, lidei com isso redefinindo a restrição para seu valor original.
Hammad Tariq
21

Eu amo código Swift limpo. Então, aqui está o código mais rígido que eu poderia criar para mover uma exibição de texto para cima / baixo com o teclado. Atualmente, ele está trabalhando em um aplicativo de produção iOS8 / 9 Swift 2.

ATUALIZAÇÃO (março de 2016): acabei de reforçar meu código anterior o máximo possível. Além disso, existem várias respostas populares aqui que codificam a altura do teclado e os parâmetros de animação. Não há necessidade disso, sem mencionar que os números nessas respostas nem sempre estão alinhados com os valores reais que estou vendo no meu 6s + iOS9 (altura do teclado de 226, duração de 0,25 e curva de animação de 7). De qualquer forma, quase não há código extra para obter esses valores diretamente do sistema. Ver abaixo.

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 cobre o caso mais comentado / geral. No entanto, mais códigos podem ser necessários para lidar com diferentes orientações e / ou teclados personalizados. Aqui está um artigo detalhado sobre o trabalho com o teclado iOS. Se você precisar lidar com todos os cenários, isso pode ajudar.

scootermg
fonte
Parece ser o Swift 1.1 e acho que não será compilado no Swift 1.2 porque ele usa aspara lançamentos de força. as!pode funcionar, mas como você pode ver em outro lugar nesta página, evito lançamentos de força e desembrulhar-me.
Joseph Lord
Compila agora no Swift 1.2. E adicionei um comentário ao código re: o desembrulhar forçado. Felicidades.
scootermg 03/03
Opa Eu quis dizer Swift 2. #
scootermg 03/03
Dependendo de como você vinculou o seu baseConstraint, pode ser em baseConstraint.constant = moveUp ? keyboardHeight : 0vez de baseConstraint.constant = moveUp ? -keyboardHeight : 0.
precisa saber é o seguinte
15

Editar : eu recomendo uma solução mais fácil e limpa. Apenas altere a classe da restrição de espaçamento inferior para KeyboardLayoutConstraint . Ele será expandido automaticamente para a altura do teclado.


Esta é uma versão aprimorada da resposta de @JosephLord.

Conforme testado no iOS 8.3 iPad Simulator, Portrait. Xcode6.3 beta4, achei sua resposta não funciona quando o teclado está escondido, porque UIKeyboardFrameEndUserInfoKeyé "NSRect: {{0, 1024}, {768, 264}}";. A altura nunca é 0.

Isso remonta ao uso do tradicional UIKeyboardWillShowNotificatione UIKeyboardWillHideNotificationpara saber melhor quando o teclado está oculto, em vez de depender da altura do quadro final. UIKeyboardWillShowNotificationtambém é enviado quando o quadro do teclado é alterado, para cobrir todos os 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
fonte
Você poderia explicar sua edição? Não consegui fazê-lo funcionar. Eu tenho um UIScrollView com um botão na parte inferior. Eu coloquei a classe na restrição da margem inferior na parte inferior.
21417 schw4ndi
@ schw4ndi a quais pontos de vista suas restrições inferiores estão ligadas? ele deve estar conectando a parte inferior do scrollview à parte inferior da superview do scrollview.
Hlung
Oh, obrigado, eu tinha a restrição entre o botão ea scrollView
schw4ndi
9

Estou trabalhando com o swift 4 e resolvi esse problema sem usar nenhuma restrição extra extra, veja meu código aqui. ele está realmente trabalhando no meu caso

1) Adicionar observador de notificação no carregamento carregado

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) Remova o Notification Observer como

deinit {
        NotificationCenter.default.removeObserver(self)
    }

3) Adicione métodos de apresentação / ocultação de 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) Adicione delegado textfeild e adicione touchesBegan métodos. Útil para ocultar o teclado quando tocar fora do textfeild na tela

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

    }
pansora abhay
fonte
precisa serUIKeyboardFrameEndUserInfoKey
Micro
1
NSNotification.Name.UIKeyboardWillShow é renomeado para UIResponder.keyboardWillShowNotificationtambém UIKeyboardFrameBeginUserInfoKey paraUIResponder.keyboardFrameBeginUserInfoKey
smj
7

Esta é uma versão aprimorada da resposta de Joseph Joseph e @ Hlung. Pode ser aplicado se você possui a barra de tabulação ou não. E isso restauraria perfeitamente a exibição movida pelo teclado para a posição 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
fonte
6

A maneira mais fácil que não requer nenhum código:

  1. Faça o download do KeyboardLayoutConstraint.swift e adicione (arraste e solte) o arquivo ao seu projeto, se você ainda não estiver usando a estrutura de animação do Spring.
  2. No storyboard, crie uma restrição inferior para o objeto / exibição / campo de texto, selecione a restrição (clique duas vezes nela) e, no Identity Inspector, altere sua classe de NSLayoutConstraint para KeyboardLayoutConstraint.
  3. Feito!

O objeto se move automaticamente para cima com o teclado, em sincronia.

gammachill
fonte
2
Ótima solução! Mas você precisa do Safe Area.Bottom como o primeiro item da restrição (não funcionou para mim quando foi o segundo item). E funcionou melhor com a constante definida como 0, pois preserva a constante e a ajusta, em vez de apenas movê-la o suficiente para mostrar o campo e o teclado.
Mythlandia 13/03/19
Você tem a versão do KeyboardLayoutConstraint swift4?
jeet.chanchawat
6

Criei um protocolo Swift 3 para lidar com a aparência / desaparecimento do 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)
  }

}

Em seguida, para implementá-lo em um UIViewController, faça o seguinte:

  • deixe o viewController estar em conformidade com este protocolo:

    class FormMailVC: UIViewControlle, KeyboardHandler {
  • comece a observar as alterações do teclado no viewWillAppear:

    // MARK: - View controller life cycle
    override func viewWillAppear(_ animated: Bool) {
      super.viewWillAppear(animated)
      startObservingKeyboardChanges()
    }
  • pare de observar as alterações do teclado no modo viewWillDisappear:

    override func viewWillDisappear(_ animated: Bool) {
      super.viewWillDisappear(animated)
      stopObservingKeyboardChanges()
    }
  • crie um IBOutlet para a restrição inferior a partir do storyboard:

    // NSLayoutConstraints
    @IBOutlet weak var bottomConstraint: NSLayoutConstraint!

    (Eu recomendo que toda a sua interface do usuário seja incorporada a um "contentView" e vincule a essa propriedade a restrição inferior deste contentView ao guia de layout inferior) Restrição inferior da visualização de conteúdo

  • altere a prioridade da restrição superior para 250 (baixa)

Restrição principal da visualização de conteúdo

Isso permite que toda a exibição do conteúdo deslize para cima quando o teclado aparecer. A prioridade deve ser menor do que qualquer outra prioridade de restrição nas subvisões, incluindo conteúdo que abraça prioridades / prioridades de resistência à compactação de conteúdo.

  • Verifique se o seu Autolayout possui restrições suficientes para determinar como o contentView deve deslizar para cima.

Pode ser necessário adicionar uma restrição "maior que igual" para isso: restrição "maior que igual"

E aqui está você! Sem teclado

Com teclado

Frédéric Adda
fonte
Funcionou se você colocar "Relação = Igual" também, sem avisos.
Valtoni Boaventura
Se você colocar um relacionamento igual, ele poderá funcionar apenas em situações específicas. Em outros, você pode ter um aviso de inconsistência no layout automático. Depende do seu próprio layout. Por isso eu disse "você pode precisar".
Frédéric Adda
Ok Frédéric, concordou. Foi uma ótima solução!
Valtoni Boaventura
ausenteimport UIKit
Mirko
6

Essa UIViewController extensão simples pode ser usada

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

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

Solução 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
fonte
2
muito mais "Swift" do que as outras soluções / funciona muito bem / reutilizável em todos os controladores sem reescrever tudo -> definitivamente o melhor aqui :) :)
Tib
Eu tentei, mas sem chance, é necessário o UIScrollView ou o quê?
erdemgc
@erdemgc você viu Exemplo de uso? Tudo que você precisa é apenas UIViewControlller + addKeyboardChangeFrameObserver e, em seguida, não se esqueça de removê-lo
ale_stro
O removeKeyboardObserver()método aqui não remove realmente o observador. Se você não chamar isso, verá um Invalid conditions for UIKeyboardWillChangeFrameNotificationno console a partir do método add. Se você ligar para isso, verá o mesmo erro, o que significa que o observador não será removido. A documentação declara "Para cancelar o registro de observações, você passa o objeto retornado por este método para removeObserver(_:)". Portanto, o que você faz é salvar o objeto retornado por esse método e depois transmiti-lo quando desejar remover o observador.
Huy-Anh Hoang
Na verdade, quando o scrollview é carregado, o limite é atribuído a um valor e você não pode detectar se um teclado ficará oculto se o quadro do teclado cruzar com o limite.
James Kim
5

Você pode usar esta biblioteca e apenas uma linha de código em appDidFinishedLaunching e você será concluída.

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

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

IQKeyboardManager - ajuste a visualização sempre que o teclado aparecer no link - https://github.com/hackiftekhar/IQKeyboardManager

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


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

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

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

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

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

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

    self.view.frame = viewFrame

    UIView.commitAnimations()
}


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

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

    UIView.setAnimationDuration(NSTimeInterval(MoveKeyboard.KEYBOARD_ANIMATION_DURATION))

    self.view.frame = viewFrame

    UIView.commitAnimations()

}

E, finalmente, como estamos usando métodos delegados

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

refatorado do uso do objetivo-c http://www.cocoawithlove.com/2008/10/sliding-uitextfields-around-to-avoid.html

Solomon Ayoola
fonte
Essa solução funcionou para mim, embora eu tenha que fazer algum trabalho adicional: declarar uma var animateDistance: CGFloat!vantagem Eu tive que lidar com o UIKeyboardWillHideNotification para quando o usuário pressionar o botão ocultar o teclado.
Rhuantavan
4

Outra solução que não depende de autolayout, restrições ou quaisquer saídas. O que você precisa é de seus campos em uma visualização de rolagem.

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
fonte
1
Ele mostra a tela preta depois de UIKeyboardWillShowNotificationchamada.
Sachin Kumaram 12/08/16
4

Aqui está a minha versão de uma solução para o Swift 2.2:

Primeiro registro para as notificações Mostrar / Ocultar do 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)

Em seguida, nos métodos correspondentes a essas notificações, mova a visualização principal para cima ou para baixo

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
}

O truque está na parte "keyboardWillShow", que recebe chamadas sempre que a "Barra de sugestões do QuickType" é expandida ou recolhida. Em seguida, sempre definimos a coordenada y da vista principal, que é igual ao valor negativo da altura total do teclado (com ou sem a parte "Barra de tipos rápidos").

No final, não se esqueça de remover os observadores

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

A seguir, é uma solução simples, na qual o campo de texto tem uma restrição que o vincula ao guia de layout inferior. Ele simplesmente adiciona a altura do teclado à constante da restrição.

// 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
fonte
3

Bem, acho que talvez seja tarde demais, mas encontrei outra versão simples da resposta de Saqib. Estou usando o Autolayout com restrições. Eu tenho uma visão pequena dentro de outra visão principal com campos de nome de usuário e senha. Em vez de alterar a coordenada y da visualização, estou salvando o valor da restrição original em uma variável e alterando a constante da restrição para algum valor e novamente depois que o teclado é descartado, estou configurando a restrição para a original. Dessa forma, evita-se o problema que a resposta de Saqib tem (a visão continua subindo e não para). Abaixo está o meu 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
fonte
O método keyboardWillShow interno verifica a condição se self.centerYConstraint.constant == self.originalCenterYConstraint, em seguida, possui uma linha de código entre essa condição. OriginalCenterYContraint é o valor original de centerYContraint que estou armazenando no viewdidload. Isso funcionou para mim.
Sashi
3

Resposta Swift 4.x, mesclando respostas de @ Joseph Joseph e @ Isuru. bottomConstraintrepresenta a restrição inferior da visualização que você deseja 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
fonte
2

Eu fiz da seguinte maneira:

Isso é útil quando a visão geral do campo de texto é vista

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
fonte
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
fonte
1
Use o código acima para mover o campo de texto acima do teclado no swift 2.2, pois funcionará bem. Espero que ajude alguém.
Kamalkumar.E
1

Eu fiz da seguinte maneira:

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
fonte
1

Se você é como eu, que já tentou todas as soluções acima e ainda assim o seu problema não está resolvido, eu tenho uma ótima solução para você que funciona como um encanto. Primeiro, quero esclarecer algumas coisas sobre algumas das soluções mencionadas acima.

  1. No meu caso, o IQkeyboardmanager estava funcionando apenas quando não havia um layout automático aplicado aos elementos; se aplicado, o gerenciador do IQkeyboard não funcionará da maneira que pensamos.
  2. A mesma coisa com o movimento ascendente de auto-visão.
  3. Eu escrevi um cabeçalho objetivo c com um rápido suporte para empurrar o UITexfield para cima quando o usuário clica nele, resolvendo o problema do teclado que cobre o UITextfield: https://github.com/coolvasanth/smart_keyboard .
  4. Quem tem um nível intermediário ou superior no desenvolvimento de aplicativos para iOS pode entender facilmente o repositório e implementá-lo. Muito bem sucedida
Vasanth
fonte
1

Aqui está uma solução genérica para todas as etapas do TextField -

1) Crie um ViewController comum que seja estendido por outros 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) Crie uma extensão do UIViewController e o TextField atualmente ativo

//get active text field

extensão 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
fonte
Por algum motivo, eu estava tendo um problema na função keyboardWillShow, o keyboardSize estava ficando com tamanho errado após a primeira alternância do teclado (a primeira alternância possui o quadro correto). Eu consertei isso alterando-o para proteger como user? NSValue else {return} deixe keyboardFrame = keyboardSize.cgRectValue se self.view.frame.origin.y == 0 {self.view.frame.origin.y - = getMoveableDistance (keyboarHeight: keyboardFrame.height)} .Espero que isso ajude se alguém tiver o mesmo problema :)
Youstanzr 22/03
1

Muito simples e sem necessidade de codificar mais. Basta adicionar pod 'IQKeyboardManagerSwift'seu podfile e, na sua AppDelegatepágina, adicionar o código abaixo.

import IQKeyboardManagerSwift

e no didFinishLaunchingWithOptions()tipo de método

IQKeyboardManager.shared.enable = true

é isso. verifique este link de vídeo para entender melhor https://youtu.be/eOM94K1ZWN8 Espero que isso ajude você.

Raghib Arshi
fonte
Funciona para o TextView e como posso alterar o título da chave de retorno "Concluído"?
tdt kien 19/01
Vá para: - "IQKeyboardManager.m" Substitua esta linha (linha nº 968): - [textField addDoneOnKeyboardWithTarget: ação automática: @selector (doneAction :) shouldShowPlaceholder: _shouldShowTextFieldPlaceholder] com este: - [textField addRightButtonOnKeyboardWithTextName: " ação própria: @selector (doneAction :) shouldShowPlaceholder: _shouldShowTextFieldPlaceholder]; E ainda não tentei a visualização de texto, espero que isso ajude você.
Raghib Arshi 20/01
1

Código completo para gerenciar o 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
fonte
0

Modifiquei a solução @Simpa um pouco .........

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
        })
    }
}
Cloy
fonte
0

Nenhum deles funcionou e acabei usando inserções de conteúdo para mover minha visualização quando o teclado aparecer.

Nota: eu estava usando um UITableView

Solução referenciada @ keyboard-content-offset que foi inteiramente escrita no objetivo C, a solução abaixo é Swift limpa.

Adicione o observador de notificações @ 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 obter o tamanho do teclado, primeiro obtemos o dicionário userInfo do objeto de notificação, que armazena quaisquer objetos adicionais que nosso destinatário possa usar.

Nesse dicionário, podemos obter o objeto CGRect que descreve o quadro do teclado usando a tecla UIKeyboardFrameBeginUserInfoKey.

Aplique a inserção de conteúdo para o método table view @ 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 o método view @ keyboardWillBeHidden

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

    yourTableView.contentInset = UIEdgeInsetsZero;

    yourTableView.scrollIndicatorInsets = UIEdgeInsetsZero;
}

Se você quiser manter a orientação do dispositivo também em consideração, use instruções condicionais para adaptar o código às suas necessidades.

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

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

insira a descrição da imagem aqui

Zany
fonte
0

A solução Swift 4 que uso usa o tamanho do teclado. Substitua serverStatusStackViewpor qualquer que seja sua visão, por exemplo 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)
}
AT
fonte