正确检测键盘的外观和消失

我有一个包含三个文本字段的注册屏幕:一个用于用户名,另外两个用于密码(安全文本输入)。 我设置了监听器来确定键盘何时出现并消失以相应地移动我的视图,以便键盘不会阻止输入小部件。

在我的viewDidLoad()中:

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

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

选择器功能:

@objc func keyboardWillShow(notification: NSNotification) {
    let userInfo = notification.userInfo
    var keyboardFrame:CGRect = (userInfo[UIKeyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
    keyboardFrame = self.view.convert(keyboardFrame, from: nil)
    movement = (keyboardFrame.size.height/2 + 20)
    print("Keyboard Appeared with a height of (movement) including 20 offset")
    UIView.animate(withDuration: 0.3, animations: {
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: self.movement * -1.0) //dy is negative for upward movement
    })
}

@objc  func keyboardWillHide(notification: NSNotification)               
    print("Keyboard Disappeared with a height of (movement) offset included")
    UIView.animate(withDuration: 0.3, animations: {
        self.view.frame = self.view.frame.offsetBy(dx: 0, dy: self.movement)
    })
}

而且,在文本字段代表设置为视图控制器的情况下,我有:

extension SignUpViewController: UITextFieldDelegate {

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if textField == textFieldUsername {
            textFieldPassword.becomeFirstResponder()
        } else if textField == textFieldPassword {
            textFieldPasswordConfirm.becomeFirstResponder()
       } else {
            textFieldPasswordConfirm.resignFirstResponder()
       }
       return true
    }
}

最后,我还有一个轻触gesturelistener,它决定用户在屏幕上的任何位置点击以消除键盘。 为此我使用了:

view.endEditing(true)

当我点击一个文本框时,keyboardWillShow()被调用,当我点击屏幕外的任何地方时,keyboardWillHide()被调用。 这是预期的。

但是,当我使用返回键从一个文本字段切换到另一个文本字段时,虽然键盘没有从屏幕消失,但仍调用keyboardWillShow()。

另外,当我在文本框中输入一些文本时,keyboardWillShow()被随机调用。

我怎样才能阻止这些不需要的键盘通知?

编辑

在阅读@rmaddy和@Adam Eberbach的答案之后,我想出了不同的方法。 首先我定义一个可滚动视图的协议,并在视图控制器中实现它,我希望它具有滚动效果:

protocol ScrollableProtocol {
    var viewScrolled: Bool { get}
    func checkScroll()
    func performScroll(direction: Int)
}

然后在视图控制器中:

private var activeTextField: UITextField?
var isScrolled: Bool = false
let offset: CGFloat = 155.00 //for now I'm defining this value as a constant which is supposed to be less than the height of the keyboard

协议的实施:

extension SignUpViewController: ScrollableProtocol {

    var viewScrolled: Bool {
        get {
            return isScrolled
        }
    }

    func checkScroll() {
        if !viewScrolled {
            performScroll(direction: 0)
            isScrolled = !isScrolled
        } 
    }

    func registerTapListener() {
        let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(clearKeyboard))
        view.addGestureRecognizer(tap)
    }

    @objc func clearKeyboard() {
        activeTextField?.resignFirstResponder()
        if viewScrolled {
            performScroll(direction: 1)
            isScrolled = !isScrolled
        }
    }

    func performScroll(direction: Int) {
        //0 -> scroll up, 1 -> scroll down
        if direction == 0 {
            UIView.animate(withDuration: 0.3, animations: {
            self.view.frame = self.view.frame.offsetBy(dx: 0, dy: self.offset * -1)
            })
        } else if direction == 1 {
            UIView.animate(withDuration: 0.3, animations: {
            self.view.frame = self.view.frame.offsetBy(dx: 0, dy: self.offset)
            })
        }
    }
}

UITextField委托方法:

extension SignUpViewController: UITextFieldDelegate {

    func textFieldDidBeginEditing(_ textField: UITextField) {
        activeTextField = textField
        checkScroll()
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if textField == textFieldUsername {
            textFieldPassword.becomeFirstResponder()
        } else if textField == textFieldPassword {
            textFieldPasswordConfirm.becomeFirstResponder()
        } else {
            textFieldPasswordConfirm.resignFirstResponder()
            performScroll(direction: 1)
            isScrolled = !isScrolled
        }
        return true
    }
}

最后,viewDidLoad方法:

override func viewDidLoad() {
    super.viewDidLoad()    
    registerTapListener()
    textFieldUsername.delegate = self
    textFieldPassword.delegate = self
    textFieldPasswordConfirm.delegate = self
}

这是正确的还是复杂的方法?


即使您实际上看不到键盘消失并重新出现,更改第一响应者也应该导致键盘事件发生。 每个文本字段可以有不同的inputView或不同的键盘类型。 而每个文本字段可以有不同的inputAccessoryView 。 所以隐藏和显示事件应该发生,因为每个不同的文本字段都是第一响应者。 这使您有机会处理不同大小的键盘和配件视图。

您应该更改keyboardWillShowkeyboardWillHide中的动画持续时间以匹配keyboardWillHide的动画持续时间。 您可以使用UIKeyboardAnimationDurationUserInfoKeyUIKeyboardAnimationCurveUserInfoKey从通知的userInfo获取此值。


我不相信你能; 每当iOS感觉像将它们发送给您时,您都会收到通知。 每次收到查看通知时,您都应该使用绝对计算,而不是将键盘高度增加或减少到视图偏移量。

通常的过程是找出通知显示可见区域底部的位置,然后确定编辑控件相对于该位置的位置,并相应地设置一些约束以使动画视图位置通常具有相同的持续时间在显示/隐藏通知中收到。 由于您进行的计算(包括控制底部边缘和可见区域底部之间的任何边距)每次都会得到相同的数字,因此这些重复的通知不会移动任何东西。 因此,您的视图始终处于可见位置,并且用户看不到四处移动,因为您正在应用大多数时间等于现有约束常量值的约束常量值 - 因此不会发生移动。

当收到键盘显示通知并且以前不可见时,或者收到隐藏通知并且以前可见时,或者键盘高度不同时,常量值确实会更改,并且您为该视图设置动画以适应新的显示安排。

链接地址: http://www.djcxy.com/p/66731.html

上一篇: Properly detect appearance and disappearance of Keyboard

下一篇: UITextField and Keyboard Notifications