UIResponder pressesBegan无法正常注册修饰键+字符按键问题
自定义UIView中修饰键+字符键重复按压无响应问题解决
问题现象
- 单独按字符键,
pressesBegan方法可正常响应 - 按住字符键同时按其他字符键,响应正常
- 按住修饰键(Cmd/Shift等)同时按其他修饰键,响应正常
- 按住修饰键按字符键时,仅能捕获第一次按下事件,后续重复按压无响应;必须松开修饰键、按一次该字符键(此次无响应)后,才能再次响应一次修饰键+字符键的组合输入
复现代码
class SomeView: UIView { override var canBecomeFirstResponder: Bool { true } init() { super.init(frame: .zero) becomeFirstResponder() } override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) { print(presses.first?.key?.keyCode.rawValue, presses.count) } }
操作序列与输出
操作步骤
- 按住Cmd键(1)
- 按z键(2)
- 按z键(3)
- 按z键(4)
- 松开Cmd键(5)
- 按z键(6)
- 按z键(7)
- 按住Cmd键(8)
- 按z键(9)
- 按z键(10)
输出结果
Optional(227) 1 // (1) Optional(29) 1 // (2),(3)(4)无输出;(5)因未重写pressEnded无反应 Optional(29) 1 // (7),(6)无输出 Optional(227) 1 // (8) Optional(29) 1 // (9),(10)无输出
原因分析
- 未重写完整的按键生命周期方法(
pressesEnded/pressesCancelled),导致UIKit无法正确跟踪按键的按下/释放状态,造成后续事件异常。 - UIKit默认逻辑中,修饰键按住时的字符键重复按压不会重复触发
pressesBegan,这类重复事件会通过pressesChanged回调。
解决方案
方案1:补全按键生命周期方法并处理pressesChanged
通过重写所有按键事件方法并调用super,确保系统正确跟踪状态,同时用pressesChanged捕获重复按压的组合键事件:
class SomeView: UIView { override var canBecomeFirstResponder: Bool { true } init() { super.init(frame: .zero) becomeFirstResponder() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) { if let key = presses.first?.key { print("Began: \(key.keyCode.rawValue), count: \(presses.count)") } // 必须调用super,保证系统事件链正常流转 super.pressesBegan(presses, with: event) } override func pressesChanged(_ presses: Set<UIPress>, with event: UIPressesEvent?) { if let key = presses.first?.key { // 捕获修饰键+字符键的重复按压事件 print("Changed: \(key.keyCode.rawValue), count: \(presses.count)") } super.pressesChanged(presses, with: event) } override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) { super.pressesEnded(presses, with: event) } override func pressesCancelled(_ presses: Set<UIPress>, with event: UIPressesEvent?) { super.pressesCancelled(presses, with: event) } }
方案2:使用UIKeyCommand处理快捷键(推荐)
如果是处理快捷键场景,直接用UIKeyCommand更精准,支持设置重复触发:
class SomeView: UIView { override var canBecomeFirstResponder: Bool { true } // 注册组合键 override var keyCommands: [UIKeyCommand]? { return [ UIKeyCommand( input: "z", modifierFlags: .command, action: #selector(handleCmdZ), repeats: true // 允许重复触发 ) ] } init() { super.init(frame: .zero) becomeFirstResponder() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @objc private func handleCmdZ() { print("Cmd+Z 触发") } }
内容的提问来源于stack exchange,提问作者Son Nguyen
相关产品推荐
相关产品推荐

