我正在尝试在菜单栏中使用自定义
View
。视图已显示,但是文本未正确更新。
我有以下代码:
@main
struct MenuBarTestApp: SwiftUI.App {
@StateObject var model = MainModel()
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class MainModel : ObservableObject
{
private var item: NSStatusItem?
private var timer: Timer?
@Published var upper: Double = 0.0
@Published var lower: Double = 0.0
init()
{
// TODO This is a workaround. Calling this code without a small delay crashes the app immediately.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
let hostingView = NSHostingView(rootView: TestView(upper: self.upper, lower: self.lower))
let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
item.length = 96.0
item.button?.addSubview(hostingView)
hostingView.constraintToSuperview()
self.item = item
}
self.timer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) {
_ in
self.upper = Double.random(in: 0.0...1.0)
self.lower = Double.random(in: 0.0...1.0)
}
}
}
struct TestView : View
{
var upper: Double
var lower: Double
var body: some View
{
VStack(spacing: 0.0) {
HStack {
Image(systemName: "circle.fill")
.resizable()
.frame(width: 6.0, height: 6.0)
Text(self.upper.description)
.font(Font.system(size: 8.0))
}
HStack {
Image(systemName: "circle.fill")
.resizable()
.frame(width: 6.0, height: 6.0)
Text(self.lower.description)
.font(Font.system(size: 8.0))
}
}
}
}
internal extension NSView
{
func constraintToSuperview()
{
guard let superview = self.superview else { return }
self.translatesAutoresizingMaskIntoConstraints = false
self.topAnchor.constraint(equalTo: superview.topAnchor, constant: 0.0).isActive = true
self.bottomAnchor.constraint(equalTo: superview.bottomAnchor, constant: 0.0).isActive = true
self.leadingAnchor.constraint(equalTo: superview.leadingAnchor, constant: 0.0).isActive = true
self.trailingAnchor.constraint(equalTo: superview.trailingAnchor, constant: 0.0).isActive = true
}
}
Timer
正确更新两个变量。但是,菜单栏中的文本永远不会更新。我认为这可能与错误的属性包装有关,因此我尝试使用 macOS 14 中新的 @Observable
宏。但是菜单栏 View
也从未更新。
我在这里缺少什么?
执行此操作时,您正在将静态值传递给视图:
TestView(upper: self.upper, lower: self.lower)
TestView
永远没有任何理由更新,因为它是使用这些值创建的once。
要更新您的视图,最简单的方法是为其提供对您创建的
ObservableObject
的引用:
struct TestView : View
{
@ObservedObject var mainModel: MainModel
var body: some View
{
VStack(spacing: 0.0) {
HStack {
Image(systemName: "circle.fill")
.resizable()
.frame(width: 6.0, height: 6.0)
Text(mainModel.upper.description)
.font(Font.system(size: 8.0))
}
HStack {
Image(systemName: "circle.fill")
.resizable()
.frame(width: 6.0, height: 6.0)
Text(mainModel.lower.description)
.font(Font.system(size: 8.0))
}
}
}
}
然后在创建模型时将模型传递给
View
:
TestView(mainModel: self)
另一种类似的方法是保持
TestView
相同,但将其包装在了解 ObservableObject
的内容中:
struct TestViewWrapper: View {
@ObservedObject var mainModel: MainModel
var body: some View {
TestView(upper: mainModel.upper, lower: mainModel.lower)
}
}
如果您想保持
TestView
与模型隔离,您可以使用该策略。