2

我有一个仅在 macOS 状态栏中运行的 macOS 应用程序。我将 Info.plist 中的“Application is agent (UIElement)”属性更改为“YES”:

<key>LSUIElement</key>
<true/>

我有一个计时器,它每 5 秒打印一次外观的名称,如下所示:

Timer.scheduledTimer(withTimeInterval: 5, repeats: true) { _ in
    let appearance = NSAppearance.currentDrawing()
    print(appearance.name)
}

问题

当我在系统设置中切换暗/亮模式时,名称实际上并没有改变。它总是打印应用程序启动时设置的外观名称。

有没有办法听系统外观变化?

目标

我的最终目标实际上是将 an 绘制NSAttributedString到 an 上NSImage,并将其NSImage用作NSStatusItem按钮的图像。

let image: NSImage = // generate image
statusItem.button?.image = image

对于我使用的属性字符串中的文本UIColor.labelColor,它应该基于系统外观。但是,它似乎不尊重系统外观的变化。

当我在深色模式下启动应用程序然后切换到浅色模式时:

黑暗-1 黑暗-2

当我在 Light Mode 下启动应用程序然后切换到 Dark Mode 时:

轻1 光2

边注

我将 theNSAttributedString变成 anNSImage并且不NSAttributedString直接在NSStatusItem按钮上使用的attributedTitle原因是因为它在状态栏中的位置不正确

4

1 回答 1

0

绘制 a 的问题NSAttributedString是,它NSAttributedString不知道如何渲染动态颜色,例如NSColor.labelColor. 因此,它不会对外观变化做出反应。您必须使用 UI 元素。

解决方案

我通过将 a 传递NSAttributedString给 aNSTextField并将其绘制到 a 中解决了这个问题NSImage。工作得很好。

func updateStatusItemImage() {

    // Use UI element: `NSTextField`
    let attributedString: NSAttributedString = ...
    let textField = NSTextField(labelWithAttributedString: attributedString)
    textField.sizeToFit()

    // Draw the `NSTextField` into an `NSImage`
    let size = textField.frame.size
    let image = NSImage(size: size)
    image.lockFocus()
    textField.draw(textField.bounds)
    image.unlockFocus()

    // Assign the drawn image to the button of the `NSStatusItem`
    statusItem.button?.image = image
}

对 NSAppearance 更改做出反应

此外,由于NSImage不知道NSAppearance我需要通过观察effectiveAppearance按钮的属性来触发外观更改的重绘NSStatusItem

observation = statusItem.observe(\.button?.effectiveAppearance, options: []) { [weak self] _, _ in
    // Redraw 
    self?.updateStatusItemImage()
}

于 2020-12-07T09:43:47.613 回答