如何测量 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
来监听击键。跳过此步骤的实现,因为它很简单(只需覆盖 touchesBegan
、touchesMoved
、touchesEnded
)。
添加 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)
.
注意:您的手势识别器不应扩展 UITapGestureRecognizer
。 UILongPressGestureRecognizer
没问题。我们不能将多个相同类型的点击手势识别器添加到视图。
可能无法使用自定义 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
来监听击键。跳过此步骤的实现,因为它很简单(只需覆盖 touchesBegan
、touchesMoved
、touchesEnded
)。
添加 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)
.
注意:您的手势识别器不应扩展 UITapGestureRecognizer
。 UILongPressGestureRecognizer
没问题。我们不能将多个相同类型的点击手势识别器添加到视图。