【问题标题】:SwiftUI macOS menubar icon with badge带有徽章的 SwiftUI macOS 菜单栏图标
【发布时间】:2021-10-10 18:32:26
【问题描述】:

从图片中可以看出,我有一个应该以menubar 打开的程序,我想知道是否可以在menubar 中添加如图二所示的badge,以表明有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)
    }
    
}

AppDelegate

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
    }
}

【问题讨论】:

  • “我在文档中找不到任何内容”您在谈论哪种类型的通知,您正在阅读文档的哪一部分?

标签: swift macos swiftui popover nspopover


【解决方案1】:

您必须手动操作

创建自定义徽章视图。在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!)
        }
    }
}

【讨论】:

  • 如何调用它,例如在 Init 函数的 ContentView 中?我试过了,但失败了:self.StatusBarController.setBadge (num: 21)
  • 您需要引用StatusBarController 的真实实例。您可以将其作为参数传递给ContentView
  • 我做了两个测试:1) 尝试在 AppDelegate 内部调用它 applicationDidFinishLaunching 所以:statusBar = StatusBarController.init(popover) statusBar?.setBadge(num: 21)
  • 正如我所说,代码假定徽章图像(红色圆圈)在资产目录中。
  • 2) 做这样的事情:i.stack.imgur.com/g1h6s.png 创建一个实例并访问它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-24
  • 2014-12-23
  • 2018-10-31
  • 1970-01-01
  • 2021-02-06
相关资源
最近更新 更多