【发布时间】:2025-12-18 11:05:01
【问题描述】:
如图所示,我有一个弹出框。
我必须确保当屏幕模式发生变化时,暗模式或亮模式,弹出框的颜色会发生变化。
颜色取自资产,如下所示:
NSColor(named: "backgroundTheme")?.withAlphaComponent(1)
从代码中可以看出,在 init 函数中启动弹出框时,我相应地分配了颜色。
如何拦截模式的变化?
你能帮帮我吗?
AppDelegate:
import Cocoa
import SwiftUI
@main
class AppDelegate: NSObject, NSApplicationDelegate {
var popover = NSPopover.init()
var statusBar: StatusBarController?
func applicationDidFinishLaunching(_ aNotification: Notification) {
let contentView = ContentView()
popover.contentSize = NSSize(width: 560, height: 360)
popover.contentViewController = NSHostingController(rootView: contentView)
statusBar = StatusBarController.init(popover)
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}
状态栏控制器:
import AppKit
import SwiftUI
extension NSPopover {
private struct Keys {
static var backgroundViewKey = "backgroundKey"
}
private var backgroundView: NSView {
let bgView = objc_getAssociatedObject(self, &Keys.backgroundViewKey) as? NSView
if let view = bgView {
return view
}
let view = NSView()
objc_setAssociatedObject(self, &Keys.backgroundViewKey, view, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
NotificationCenter.default.addObserver(self, selector: #selector(popoverWillOpen(_:)), name: NSPopover.willShowNotification, object: nil)
return view
}
@objc private func popoverWillOpen(_ notification: Notification) {
if backgroundView.superview == nil {
if let contentView = contentViewController?.view, let frameView = contentView.superview {
frameView.wantsLayer = true
backgroundView.frame = NSInsetRect(frameView.frame, 1, 1)
backgroundView.autoresizingMask = [.width, .height]
frameView.addSubview(backgroundView, positioned: .below, relativeTo: contentView)
}
}
}
var backgroundColor: NSColor? {
get {
if let bgColor = backgroundView.layer?.backgroundColor {
return NSColor(cgColor: bgColor)
}
return nil
}
set {
backgroundView.wantsLayer = true
backgroundView.layer?.backgroundColor = newValue?.cgColor
}
}
}
class StatusBarController {
private var popover: NSPopover
private var statusBar: NSStatusBar
var statusItem: NSStatusItem
init(_ popover: NSPopover) {
self.popover = popover
self.popover.backgroundColor = NSColor(named: "backgroundTheme")?.withAlphaComponent(1)
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)
}
}
【问题讨论】:
-
@ElTomato:它似乎有效,但在 interfaceModeChanged 内,当我更改时我无法恢复背景主题颜色。
popover.backgroundColor = NSColor(named:" backgroundTheme")?.withAlphaComponent(1) -
在 macOS 中,观察外观变化需要两个步骤。您可以在视图 (SwiftUI) 或视图控制器 (Cocoa) 中查看当前外观(浅绿色或深色)。这还不够。您还需要让应用程序通过
AppDelegate观察外观变化,以便它知道用户何时使用系统偏好进行更改。 -
@ElTomato:那我该如何解决这个问题呢?
-
"backgroundView.layer?.backgroundColor = newValue?.cgColor" -> 你不应该在颜色设置器中设置图层颜色。使用 setNeedsUpdatelayer 并在更新图层中进行图层颜色分配。我建议观看 WWDC18 Advanced Dark Mode,其中提到了为什么你的代码不起作用;developer.apple.com/videos/play/wwdc2018/218
标签: swift macos popover appkit nspopover