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

Mac Catalyst Swift应用创建动态系统菜单的报错问题咨询

问题与解决方案

问题说明

  • 将buildMenu函数放入自定义类时出现两个错误:
    1. Method does not override any method from its superclass
    2. Cannot find type 'UIMenuBuilder' in scope
  • 该函数放在AppDelegate中可正常运行,尝试让自定义类继承AppDelegate,代码如下:
class MidiMenu: AppDelegate {
    override func buildMenu(with builder: UIMenuBuilder) {
        super.buildMenu(with: builder)
        builder.remove(menu: .file)
    }
}

需求目标

实现动态菜单,流程如下:

  1. 添加"Profiles"系统菜单选项;
  2. 点击"Create New Profile"弹出带输入框的弹窗;
  3. 输入新配置文件名并确认后,在"Profiles"菜单下生成新的子菜单项。

错误原因

  1. Cannot find type 'UIMenuBuilder' in scope:自定义类未导入对应框架(iOS/macOS Catalyst需导入UIKit,原生macOS需用NSMenuBuilder并导入AppKit),导致无法识别该类型。
  2. Method does not override any method from its superclass:继承AppDelegate的做法不符合设计规范,AppDelegate是应用委托类,不应被继承来实现菜单逻辑;且AppDelegate的buildMenu方法继承自UIResponder,自定义类的继承方式无法正确重写该方法。

正确实现方案

步骤1:创建菜单管理类

封装菜单构建、弹窗展示和菜单更新逻辑:

import UIKit // 原生macOS替换为import AppKit,UIMenuBuilder改为NSMenuBuilder

class MidiMenuManager {
    static let shared = MidiMenuManager()
    
    // 存储配置文件名称
    private var profileNames: [String] = []
    
    // 构建Profiles菜单
    func buildProfilesMenu() -> UIMenu {
        var menuActions: [UIAction] = []
        
        // 添加已存在的配置选项
        for name in profileNames {
            let action = UIAction(title: name) { _ in
                // 处理配置文件选中逻辑
                print("Selected profile: \(name)")
            }
            menuActions.append(action)
        }
        
        // 添加创建新配置的选项
        let createAction = UIAction(title: "Create New Profile") { [weak self] _ in
            self?.showCreateProfileAlert()
        }
        menuActions.append(createAction)
        
        return UIMenu(title: "Profiles", children: menuActions)
    }
    
    // 弹出创建配置的弹窗
    private func showCreateProfileAlert() {
        let alert = UIAlertController(title: "Create New Profile", message: "Enter profile name", preferredStyle: .alert)
        alert.addTextField { textField in
            textField.placeholder = "Profile name"
        }
        
        let confirmAction = UIAlertAction(title: "Create", style: .default) { [weak self] _ in
            guard let name = alert.textFields?.first?.text, !name.isEmpty else { return }
            self?.profileNames.append(name)
            // 触发菜单更新
            self?.updateMenu()
        }
        let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
        
        alert.addAction(confirmAction)
        alert.addAction(cancelAction)
        
        // 获取根控制器展示弹窗
        if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
           let rootVC = windowScene.windows.first?.rootViewController {
            rootVC.present(alert, animated: true)
        }
    }
    
    // 更新应用菜单
    private func updateMenu() {
        UIApplication.shared.keyWindow?.windowScene?.menuBuilder?.rebuild()
    }
}

步骤2:在AppDelegate中集成菜单逻辑

在AppDelegate的buildMenu方法中调用管理类构建菜单:

import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        return true
    }

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
    }
    
    override func buildMenu(with builder: UIMenuBuilder) {
        super.buildMenu(with: builder)
        
        // 移除不需要的系统菜单
        builder.remove(menu: .file)
        builder.remove(menu: .edit)
        builder.remove(menu: .format)
        
        // 添加自定义Profiles菜单
        let profilesMenu = MidiMenuManager.shared.buildProfilesMenu()
        builder.insertSibling(profilesMenu, afterMenu: .application)
    }
}

方案说明

  • 采用单例模式的菜单管理类,实现职责分离,避免污染AppDelegate的代码;
  • 新增配置后调用menuBuilder?.rebuild()触发菜单重新构建,自动更新子菜单项;
  • 确保导入对应框架,解决类型找不到的问题;
  • 摒弃继承AppDelegate的错误做法,遵循iOS/macOS应用的设计规范。

内容的提问来源于stack exchange,提问作者jerry seigle

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 14:35:36