You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

操作序列与输出

操作步骤

  1. 按住Cmd键(1)
  2. 按z键(2)
  3. 按z键(3)
  4. 按z键(4)
  5. 松开Cmd键(5)
  6. 按z键(6)
  7. 按z键(7)
  8. 按住Cmd键(8)
  9. 按z键(9)
  10. 按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)无输出

原因分析

  1. 未重写完整的按键生命周期方法(pressesEnded/pressesCancelled),导致UIKit无法正确跟踪按键的按下/释放状态,造成后续事件异常。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 11:30:41