首页 > 解决方案 > Swift - 使用 UIKeyboardWillChangeFrame 时关闭键盘并调用 TouchUpInside 按钮同时不起作用

问题描述

我正在使用 swift 并且在使用 TouchUpInside 时遇到问题:如果我使用的是 UIKeyboardWillChangeFrame 或 UIKeyboardWillShow/UIKeyboardWillHide,并且键盘正在显示,并且当最初显示键盘时,我尝试按下的按钮位于键盘后面。(如果我向下滚动到按钮直到可见并按下,则不会调用 touchUpInside)。

无论键盘是否显示,TouchDown 似乎都可以始终如一地工作,但不会调用 TouchUpInside。如果最初显示键盘时按钮位于键盘顶部上方,则 TouchUpInside 有效。我正在使用keyboardNotification 设置我的scrollView 下方的视图高度,以便在显示键盘时提升我的scrollView。从我所见,通常只有当按钮是 scrollView 中的最后一个元素时(因此在显示键盘时可能位于键盘后面)。

@IBOutlet var keyboardHeightLayoutConstraint: NSLayoutConstraint?
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var saveButton: UIButton!
@IBAction func saveTouchUpInside(_ sender: UIButton) {
   print("touchupinside = does not work")
}
@objc func saveTouchDown(notification:NSNotification){
   print("touchdown = works")
}

视图将出现:

textField.delegate = self

NotificationCenter.default.addObserver(self,selector:#selector(self.keyboardNotification(notification:)),name: 

NSNotification.Name.UIKeyboardWillChangeFrame,object: nil)
self.saveButton.addTarget(self, action:#selector(ViewController.saveTouchDown(notification:)), for: .touchDown)

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

我想关闭键盘并同时调用 saveTouchUpInside,而不使用 TouchDown。

标签: swiftuibuttonuikeyboardnotificationcentertouch-up-inside

解决方案


我将键盘交互抽象为一个单独的类,这样我的控制器就不会变得臃肿(也遵循separation of concerns)。这是我使用的键盘管理器类。

import UIKit


/**
*  To adjust the scroll view associated with the displayed view to accommodate
*  the display of keyboard so that the view gets adjusted accordingly without getting hidden
*/
class KeyboardManager {

    private var scrollView: UIScrollView

    /**
    *  -parameter scrollView: ScrollView that need to be adjusted so that it does not get clipped by the presence of the keyboard
    */
    init(scrollView: UIScrollView) {

        self.scrollView = scrollView

        let notificationCenter = NotificationCenter.default
        notificationCenter.addObserver(self,
                                                                     selector: #selector(adjustForKeyboard),
                                                                     name: UIResponder.keyboardWillHideNotification, object: nil)
        notificationCenter.addObserver(self,
                                                                     selector: #selector(adjustForKeyboard),
                                                                     name: UIResponder.keyboardDidChangeFrameNotification, object: nil)
    }

    /**
    * Indicates that the on-screen keyboard is about to be presented.
    *  -parameter notification: Contains animation and frame details on the keyboard
    *
    */
    @objc func adjustForKeyboard(notification: Notification) {

        guard let containedView = scrollView.superview else { return }

        let userInfo = notification.userInfo!
        let keyboardScreenEndFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as! NSValue).cgRectValue
        let keyboardViewEndFrame = containedView.convert(keyboardScreenEndFrame, to: containedView.window)

        let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as! NSNumber
        let rawAnimationCurveValue = (userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as! NSNumber).uintValue

        UIView.animate(withDuration: TimeInterval(truncating: duration),
                                     delay: 0,
                                     options: [UIView.AnimationOptions(rawValue: rawAnimationCurveValue)],
                                     animations: {

                                        if notification.name == UIResponder.keyboardWillHideNotification {
                                            self.scrollView.contentInset = UIEdgeInsets.zero
                                        } else {
                                            self.scrollView.contentInset = UIEdgeInsets(top: 0,
                                                                                                                                    left: 0,
                                                                                                                                    bottom: keyboardViewEndFrame.height,
                                                                                                                                    right: 0)
                                        }

                                        self.scrollView.scrollIndicatorInsets = self.scrollView.contentInset

        },
                                     completion: nil)
    }

    deinit {
        let notificationCenter = NotificationCenter.default
        notificationCenter.removeObserver(self)
    }

}

它的用法是这样的

  • 创建对键盘管理器的引用
 private var keyboardManager: KeyboardManager!
  • 并分配键盘管理器类,如下所示,您正在使用的滚动视图在viewDidLoad哪里self.scrollView
 self.keyboardManager = KeyboardManager(scrollView: self.scrollView)

这应该解决这个问题。如果这不起作用,可能一个示例项目可能有助于深入研究。


推荐阅读