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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 21:06:11