首页 > 解决方案 > 带有徽章的 SwiftUI macOS 菜单栏图标

问题描述

正如您从图像中看到的那样,我有一个应该打开的程序menubar,我想知道是否可以badge在图像 2 中添加一个如图 2 所示的menubar程序,以表明存在notifications.

我在文档上找不到任何东西。

你能帮我个忙吗?

在此处输入图像描述

在此处输入图像描述

状态栏控制器

import AppKit
import SwiftUI

class StatusBarController {
    @ObservedObject var userPreferences = UserPreferences.instance
    private var statusBar: NSStatusBar
    var statusItem: NSStatusItem
    private var popover: NSPopover
    
    init(_ popover: NSPopover) {
        self.popover = popover
        statusBar = NSStatusBar.init()
        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
        
        if let statusBarButton = statusItem.button {
            statusBarButton.image = #imageLiteral(resourceName: "Fork")
            statusBarButton.image?.size = NSSize(width: 18.0, height: 18.0)
            statusBarButton.image?.isTemplate = true
            statusBarButton.action = #selector(togglePopover(sender:))
            statusBarButton.target = self
            statusBarButton.imagePosition = NSControl.ImagePosition.imageLeft
        }
    }
    
    @objc func togglePopover(sender: AnyObject) {
        if(popover.isShown) {
            hidePopover(sender)
        }else {
            showPopover(sender)
        }
    }
    
    func showPopover(_ sender: AnyObject) {
        if let statusBarButton = statusItem.button {
            popover.show(relativeTo: statusBarButton.bounds, of: statusBarButton, preferredEdge: NSRectEdge.maxY)
        }
    }
    
    func hidePopover(_ sender: AnyObject) {
        popover.performClose(sender)
    }
    
}

应用委托

import Cocoa
import SwiftUI

@main
class AppDelegate: NSObject, NSApplicationDelegate {
    var statusBar: StatusBarController?
    var popover = NSPopover.init()
    
    var timer: Timer? = nil

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        let contentView = ContentView()
        popover.contentSize = NSSize(width: 360, height: 360)
        popover.contentViewController = NSHostingController(rootView: contentView)
        statusBar = StatusBarController.init(popover)
    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }
}

标签: swiftmacosswiftuipopovernspopover

解决方案


你必须手动完成

创建自定义徽章视图。在drawRect您必须使用徽章的位置和数字的大小。

class BadgeView: NSView {
    
    var number : Int {
        didSet {
            if oldValue != number { needsDisplay = true }
        }
    }
    
    init(frame frameRect: NSRect, number : Int) {
        self.number = number
        super.init(frame: frameRect)
    }
    
    required init?(coder: NSCoder) {
        self.number = 0
        super.init(coder: coder)
    }
    
    override func draw(_ dirtyRect: NSRect) {
        let fillColor = NSColor.systemRed
        let path = NSBezierPath(ovalIn: NSRect(x: 3, y: 4, width: 14, height: 14))
        fillColor.set()
        path.fill()
        let one = "\(number)"
        let attribs : [NSAttributedString.Key:Any] = [.font : NSFont.systemFont(ofSize: 11.0), .foregroundColor : NSColor.white]
        let xOrigin = (number > 9) ? 3.5 : 6.5
        one.draw(at: NSPoint(x: xOrigin, y: 4.5), withAttributes: attribs)
    }
}

在控制器类中添加一个属性和一个函数来设置数字

private var badgeView : BadgeView?

func setBadge(num : Int)
{
    if num == 0 {
        if let view = badgeView {
            view.removeFromSuperview()
            badgeView = nil
        }
    } else {
        if let badgeView = badgeView {
            badgeView.number = num
        } else {
            badgeView = BadgeView(frame: NSRect(x: 0, y: 0, width: 19, height: 22), number: num)
            statusItem.button!.addSubview(badgeView!)
        }
    }
}

推荐阅读