如何在SwiftUI中实现场景专属状态并适配commands快捷键绑定
可以通过SwiftUI提供的*焦点值(FocusedValue)*机制实现,这是官方推荐的跨焦点场景与全局命令通信的方案,具体实现步骤如下:
实现步骤
1. 定义全局焦点键
首先创建自定义的FocusedValueKey,用来作为传递当前活跃窗口状态的标识:
import SwiftUI // 自定义焦点键,类型对应你的状态控制器 struct CompilerFocusedKey: FocusedValueKey { typealias Value = UIStateController } extension FocusedValues { var currentCompiler: UIStateController? { get { self[CompilerFocusedKey.self] } set { self[CompilerFocusedKey.self] = newValue } } }
2. 给每个窗口绑定专属状态到焦点值
修改你的DocumentGroup实现,在初始化每个窗口的状态后,通过.focusedValue修饰符关联到前面定义的全局键:
@main struct QCCApp: App { var body: some Scene { DocumentGroup(newDocument: QCCDocument()) { file in let compiler = UIStateController(targetting: file) ContentView() .environmentObject(compiler) // 把当前窗口的专属状态绑定到焦点键 .focusedValue(\.currentCompiler, compiler) } .commands { CommandDelegate() } Settings { SettingsView() } } }
3. 在全局命令中读取当前活跃窗口的状态
修改CommandDelegate的实现,通过@FocusedValue属性包装器读取当前焦点窗口对应的状态:
struct CommandDelegate: Commands { // 自动读取当前活跃窗口绑定的状态,无活跃窗口时为nil @FocusedValue(\.currentCompiler) var activeCompiler var body: some Commands { CommandMenu("自定义操作") { Button("运行编译") { // 只会触发当前活跃窗口的状态方法,不会影响其他窗口 activeCompiler?.runCompile() } .keyboardShortcut("r", modifiers: .command) // 无活跃窗口时自动禁用快捷键 .disabled(activeCompiler == nil) } } }
实现原理
FocusedValue是SwiftUI专为焦点场景设计的状态传递机制:
- 每个窗口独立绑定自己的状态到焦点键,互不干扰
- 全局命令中读取的
@FocusedValue永远是当前获得焦点(处于最前方活跃状态)的窗口对应的值 - 无活跃窗口时属性自动为nil,可直接用来禁用不适用的快捷键
内容的提问来源于stack exchange,提问作者Smally
相关产品推荐
相关产品推荐

