如何测量 iOS 系统键盘上的触摸压力(击键)?

How to measure touch pressure (keystroke) on iOS system keyboard?

可能无法使用自定义 UIGestureRecognizer 来测量触摸压力,因为我们无法直接访问系统键盘的视图 [1]。如果使用自定义 inputViewController 可以实现,但对我来说这不是一个选项。我想为每个键存储按键保持时间和按键压力。你知道这样做的方法吗?

编辑:[1] 我说 "We cannot directly access the system keyboard's view." 但实际上我们可以: UIRemoteKeyboardWindow > InputSetContainerView > InputSetHostView.

我认为对 Nirav Bhatt 的回答进行改进可能会解决此问题,但它会禁用键盘视图上的触摸功能。当我触摸一个键时,它输出触摸力,但相应的字母没有输入到文本字段。

自 iOS 9 起,作为 3D 触摸的一部分,UITouch has got force property 可以让您准确了解触摸事件期间施加的压力。

您可以将其与您的自定义视图实现结合起来,将每个字母数字字符作为 UIButton 接收触摸并将其传递。

更新:

在与 UIWindow 讨价还价之后,我终于能够在苹果的标准键盘中捕获触摸事件 window。

诀窍是在 UIWindow 扩展中覆盖 hittest(也可以在 UIView 级别完成,但它会产生更广泛的影响,因为 UIView 是祖先的 UIWindow),以及 return 每当您看到来自 UIRemoteKeyboardWindow.

的触摸时触摸处理对象
extension UIWindow
{
    override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
    {
        print("kb touch")

        for touch in touches
        {
           let pressure = touch.force
           print("pressure is: \(String(describing: pressure))")
        }
    }

    override open func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        print("hittest")

        let view = super.hitTest(point, with: event)

        if (NSStringFromClass(type(of: self)) == "UIRemoteKeyboardWindow")
        {
            //will result in touchesBegan being called
            return self;
        }

        return view
    }
}

解决方法比我想象的要简单。对于iOS 9+系统,我们可以按照以下步骤访问系统键盘并监听击键...

首先,创建一个自定义 UIGestureRecognizer 来监听击键。跳过此步骤的实现,因为它很简单(只需覆盖 touchesBegantouchesMovedtouchesEnded)。

添加 KeyboardManager 以访问键盘视图:

final class KeyboardManager {

    static let shared = KeyboardManager()

    private init() { }

    /// Returns keyboard view on application window
    ///
    /// - Returns: Keyboard view
    func keyboardView() -> UIView? {
        for window in UIApplication.shared.windows {
            if let keyboardView = keyboardViewFromWindow(window) {
                return keyboardView
            }
        }
        return nil
    }

    /// Returns keyboard view from given window
    ///
    /// - Parameter window: Keyboard view container candidate window
    /// - Returns: Keyboard view
    func keyboardViewFromWindow(_ window: UIWindow) -> UIView? {
        if window.hasClassNameSuffix("UIRemoteKeyboardWindow") {
            let inputSetContainerView = window.subview(withSuffix: "InputSetContainerView")
            let inputSetHostView = inputSetContainerView?.subview(withSuffix: "InputSetHostView")
            return inputSetHostView
        }

        return nil
    }
}

UIView 上面用到的扩展函数:

@nonobjc extension UIView {

    /// Returns first found subview with given class name
    ///
    /// - Parameter className: Subview class name
    /// - Returns: First subview with given class name
    func subview(withSuffix className: String) -> UIView? {
        return subviews.first {
            [=11=].hasClassNameSuffix(className)
        }
    }

    /// Compares suffix of view class name with given name
    ///
    /// - Parameter className: Class name that will be compared
    /// - Returns: Comparison result of view class name and given name
    func hasClassNameSuffix(_ className: String) -> Bool {
        return NSStringFromClass(type(of: self)).hasSuffix(className)
    }
}

然后,使用以下函数将您的手势识别器添加到键盘视图:

KeyboardManager.shared.keyboardView()?.addGestureRecognizer(keyboardGestureRecognizer).

注意:您的手势识别器不应扩展 UITapGestureRecognizerUILongPressGestureRecognizer 没问题。我们不能将多个相同类型的点击手势识别器添加到视图。