繁体   English   中英

如何更改 macOS 菜单栏图标?

[英]How to change a macOS menubar icon?

我正在尝试根据这个问题单击按钮更改 macOS 菜单栏图标。

但是,当我运行应用程序并单击按钮时,我得到Could not cast value of type 'SwiftUI.AppDelegate' (0x20dfafd68) to 'MenuBarTest.AppDelegate' (0x10442c580). 此行错误:

let appDelegate = NSApplication.shared.delegate as! AppDelegate

主 Swift 文件

@main
struct MenuBarTestApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var delegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class AppDelegate: NSObject, NSApplicationDelegate {
    var statusItem: NSStatusItem?
    var popOver = NSPopover()
    
    func applicationDidFinishLaunching(_ notification: Notification) {
        let menuView = MenuBarView()
        
        popOver.behavior = .transient
        popOver.animates = true
        popOver.contentViewController = NSViewController()
        popOver.contentViewController?.view = NSHostingView(rootView: menuView)
        
        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
        
        if let MenuButton = statusItem?.button {
            MenuButton.image = NSImage(named: "settings_bw")
            MenuButton.image?.size = NSSize(width: 18.0, height: 18.0)
            MenuButton.action = #selector(MenuButtonToggle)
        }
    }
    
    func setIcon(colorType:String) {
        var icon = NSImage(named: "settings_color")
        
        if colorType != "color" {
            icon = NSImage(named: "settings_bw")
        }
        
        statusItem?.button?.image = icon
    }
    
    @objc
    func MenuButtonToggle(sender: AnyObject) {
        if popOver.isShown {
            popOver.performClose(sender)
        } else {
            if let MenuButton = statusItem?.button {
                self.popOver.show(relativeTo: MenuButton.bounds, of: MenuButton, preferredEdge: NSRectEdge.minY)
                popOver.contentViewController?.view.window?.makeKey()
            }
        }
    }
}

菜单栏视图

struct MenuBarView: View {
    var body: some View {
        VStack {
            Button(action: {
                let appDelegate = NSApplication.shared.delegate as! AppDelegate
                appDelegate.setIcon(colorType: "color")
            }, label: {
                Text("Change Icon")
            })
        }
        .frame(width: 150, height: 100)
    }
}

首先,我会按要求回答问题,然后我会跟进为什么它实际上无关紧要,因为它解决了错误的问题。

在 SwiftUI 生命周期应用程序中访问 App Delegate

当您使用 SwiftUI 来管理您的应用程序生命周期时,您不会直接使用应用程序委托。 SwiftUI 会将应用程序的应用委托设置为它自己的 class, SwiftUI.AppDelegate的一个实例。 这是您在尝试将其转换为MenuBarTest.AppDelegate时所看到的。 您不能将它转换为您的 class,因为它不是您的 class 的实例。

作为向后兼容功能,SwiftUI 的应用程序委托允许您提供自己的委托实例,它将转发到该实例。 这与NSApplicationDelegateAdaptor属性包装器连接在一起。 如果您想访问一个实例,delegate实例属性是获取它的方法(不是NSApplication.shared.delegate )。

甚至不要使用 App Delegate

你可以无视我到目前为止所说的一切,因为这只是一个糟糕的设计,你不应该使用它。 App Delegate 应严格限于与管理应用程序生命周期及其与系统交互方式相关的代码。 应该是任何适合的代码的厨房垃圾抽屉。

在 App Delegate 中拥有随机代码是 AppKit 等同于将所有代码放在 C 程序的main() function 中。 这样做是为了简洁,但很少是个好主意。 这是您在教程中经常看到的内容,旨在将文件和行数保持在最低限度,以说明其他一些要点(例如,在本例中与菜单栏配置相关)。

您会注意到,此应用程序委托中的代码都没有做很多特定于应用程序生命周期的事情。 它只是调用 NSStatusBar。 这段代码可以放在任何其他适当的地方,就像任何其他有状态的 SwiftUI 代码一样。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM