Mac Catalyst下SwiftUI应用带Command+,的偏好设置菜单实现问题
问题
我有一个基于SwiftUI和Mac Catalyst开发的应用,在Mac端运行时,希望添加带有标准快捷键Command + ,的偏好设置菜单入口。由于偏好设置逻辑过于复杂,无法使用Settings Bundle实现。
简化后的实现代码如下:
import SwiftUI @main struct PreferencesMenuTestApp: App { @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: UIResponder, UIApplicationDelegate { override func buildMenu(with builder: UIMenuBuilder) { let preferencesCommand = UIKeyCommand(title: "Preferences…", action: #selector(showPreferences), input: ",", modifierFlags: .command) // let preferencesCommand = UIAction(title: "Preferences…") { action in // debugPrint("show preferences") // } let menu = UIMenu(title: "Preferences…", options: .displayInline, children: [preferencesCommand]) builder.insertSibling(menu, afterMenu: .about) } @objc func showPreferences() { debugPrint("show preferences") } }
当前遇到的问题:
- 菜单入口处于禁用状态,系统无法识别对应的选择器
- 给
AppDelegate添加@main标记后,菜单可正常启用,但应用窗口会变为空白 - 改用注释中的
UIAction实现时菜单正常工作,但UIAction无法设置快捷键,不符合需求
如何实现可正常工作的带快捷键的偏好设置菜单入口?
解决方案
问题核心是UIKeyCommand未正确关联方法的目标对象,导致系统找不到对应的@objc方法。以下两种方式可以解决:
方法一:为UIKeyCommand指定目标
直接给UIKeyCommand添加target属性指向当前AppDelegate实例,让系统明确知道调用哪个对象的方法:
override func buildMenu(with builder: UIMenuBuilder) { let preferencesCommand = UIKeyCommand(title: "Preferences…", action: #selector(showPreferences), input: ",", modifierFlags: .command, target: self) let menu = UIMenu(title: "Preferences…", options: .displayInline, children: [preferencesCommand]) builder.insertSibling(menu, afterMenu: .about) }
方法二:用UIAction绑定快捷键(更推荐)
这种方式兼顾UIAction的可用性和UIKeyCommand的快捷键功能,逻辑更清晰:
override func buildMenu(with builder: UIMenuBuilder) { let preferencesAction = UIAction(title: "Preferences…") { [weak self] _ in self?.showPreferences() } // 创建快捷键命令并关联到UIAction let keyCommand = UIKeyCommand(input: ",", modifierFlags: .command, action: #selector(UIResponder.sendAction(_:to:from:for:))) keyCommand.wantsPriorityOverSystemBehavior = true preferencesAction.attributes = .init(keyCommands: [keyCommand]) let menu = UIMenu(title: "Preferences…", options: .displayInline, children: [preferencesAction]) builder.insertSibling(menu, afterMenu: .about) }
补充说明
- 务必保留
showPreferences方法的@objc修饰,否则无法被Objective-C运行时识别 - 如果需要展示偏好设置窗口,可以在
showPreferences中添加窗口创建逻辑:
@objc func showPreferences() { let preferencesView = PreferencesView() // 你的偏好设置SwiftUI视图 let window = UIWindow() window.rootViewController = UIHostingController(rootView: preferencesView) window.title = "Preferences" window.makeKeyAndVisible() }
内容的提问来源于stack exchange,提问作者Ray Wojciechowski
相关产品推荐
相关产品推荐

