Probably there is no way to measure touch pressure using a custom UIGestureRecognizer
, since we cannot directly access the system keyboard's view [1]. It can be achieved if a custom inputViewController
is used but it's not an option for me. I want to store key hold time and key press pressure for each key. Do you know a way to do this?
Edit: [1] I said "We cannot directly access the system keyboard's view." but actually we can:
UIRemoteKeyboardWindow > InputSetContainerView > InputSetHostView
.
I think that an improvement to Nirav Bhatt's answer may solve this issue but it disables the functionality of touches on the keyboard view. When I touch on a key, it outputs force of touch but the corresponding letter isn't typed to text field.
The solution is easier than I think. For iOS 9+ systems, we can follow these steps to access system keyboard and listen keystroke...
At first, create a custom UIGestureRecognizer
to listen keystroke. The implementation of this step is skipped, since it's trivial (just override touchesBegan
, touchesMoved
, touchesEnded
).
Add KeyboardManager
to access keyboard view:
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
extension functions used above:
@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 {
$0.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)
}
}
Then, add your gesture recognizer to keyboard view using below functions:
KeyboardManager.shared.keyboardView()?.addGestureRecognizer(keyboardGestureRecognizer)
.
Note: Your gesture recognizer shouldn't extend UITapGestureRecognizer
. UILongPressGestureRecognizer
is okay. We cannot add multiple tap gesture recognizers with same type to a view.