1. Bind notification in viewDidload() and add keyboard listener
The bound keyboardWillShow will have animation, KeyboardDidShow will not have animation
func viewDidload(){ NotificationCenter.default.addObserver(self, selector: #selector(PasscodePane.keyboardWillHide(_:)),name:NSNotification.Name.UIKeyboardWillHide, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(PasscodePane.keyboardWillShow(_:)),name:NSNotification.Name.UIKeyboardWillShow, object: nil) } //freed deinit { NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil) } func keyboardWillShow(_ sender: Notification) { //Get the frame of the keyboard guard let keyboardFrame = (sender.userInfo?[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue else { return } // also return if border=0 if(keyboardFrame.height == nill || keyboardFrame.height == 0){return;} UIView.animate(withDuration: 0.1, animations: { self.containerCenterConstraint?.update(offset: -keyboardFrame.height/2) self.layoutIfNeeded() }) } func keyboardWillHide(_ sender: Notification) { UIView.animate(withDuration: 0.1, animations: { self.containerCenterConstraint?.update(offset: 0) self.layoutIfNeeded() }) }
** Fix the issue that sometimes or third-party input triggers 3 or more notifications
The principle is to get the latest height obtained within 1 second, and update the UI after 1 second
var keyb_ht:CGFloat = 0; var getKbHt_time_over:Bool = true; //Mechanism: The UI is updated only after the last height obtained within 1 second, starting from the first trigger or the first trigger after 1 second func keyboardWillShow(aNotification: NSNotification) { let userinfo: NSDictionary = aNotification.userInfo! as NSDictionary let nsValue = userinfo.object(forKey: UIKeyboardFrameEndUserInfoKey) let keyboardRec = (nsValue as AnyObject).cgRectValue let key_height = keyboardRec?.size.height if(key_height == 0){ return; } //Update the current latest height keyb_ht = key_height!; //If the UI is delayed for 1 second and the UI has not been executed, return if(getKbHt_time_over == false){ print("111") return; } print("222") getKbHt_time_over = false; DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {//Execute after 1 second print("333") //update ui self.kb_top_view?.frame.origin.y = screenHeight - self.keyb_ht - self.kb_top_view_ht; self.getKbHt_time_over = true; } }
When executing animation, if you encounter invalid setting animation time, you can use asynchronous
DispatchQueue.main.async{
}
Reference: https://stackoverflow.com/questions/37805885/how-to-create-dispatch-queue-in-swift-3
** The input box gets focus by default
textField.becomeFirstResponder()
** Firefox browser source code for using this method
class PasscodePane: UIView { let codeInputView = PasscodeInputView(passcodeSize: 4) var codeViewCenterConstraint: Constraint? var containerCenterConstraint: Constraint? fileprivate lazy var titleLabel: UILabel = { let label = UILabel() label.font = UIConstants.DefaultChromeFont label.isAccessibilityElement = true return label }() fileprivate let centerContainer = UIView() override func accessibilityElementCount() -> Int { return 1 } override func accessibilityElement(at index: Int) -> Any? { switch index { case 0: return titleLabel default: return nil } } init(title: String? = nil) { super.init(frame: CGRect.zero) backgroundColor = UIConstants.TableViewHeaderBackgroundColor titleLabel.text = title centerContainer.addSubview(titleLabel) centerContainer.addSubview(codeInputView) addSubview(centerContainer) centerContainer.snp.makeConstraints { make in make.centerX.equalTo(self) containerCenterConstraint = make.centerY.equalTo(self).constraint } titleLabel.snp.makeConstraints { make in make.centerX.equalTo(centerContainer) make.top.equalTo(centerContainer) make.bottom.equalTo(codeInputView.snp.top).offset(-PasscodeUX.TitleVerticalSpacing) } codeInputView.snp.makeConstraints { make in codeViewCenterConstraint = make.centerX.equalTo(centerContainer).constraint make.bottom.equalTo(centerContainer) make.size.equalTo(PasscodeUX.PasscodeFieldSize) } layoutIfNeeded() NotificationCenter.default.addObserver(self, selector: #selector(PasscodePane.keyboardWillHide(_:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(PasscodePane.keyboardWillShow(_:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil) } func shakePasscode() { UIView.animate(withDuration: 0.1, animations: { self.codeViewCenterConstraint?.update(offset: -10) self.layoutIfNeeded() }, completion: { complete in UIView.animate(withDuration: 0.1, animations: { self.codeViewCenterConstraint?.update(offset: 0) self.layoutIfNeeded() }) }) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } deinit { NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil) } func keyboardWillShow(_ sender: Notification) { guard let keyboardFrame = (sender.userInfo?[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue else { return } UIView.animate(withDuration: 0.1, animations: { self.containerCenterConstraint?.update(offset: -keyboardFrame.height/2) self.layoutIfNeeded() }) } func keyboardWillHide(_ sender: Notification) { UIView.animate(withDuration: 0.1, animations: { self.containerCenterConstraint?.update(offset: 0) self.layoutIfNeeded() }) } }