嵌入 NSHostingView 时 SwiftUI 视图不更新

问题描述 投票:0回答:1

我正在尝试在菜单栏中使用自定义

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
也从未更新。

我在这里缺少什么?

swift swiftui menubar
1个回答
0
投票

执行此操作时,您正在将静态值传递给视图:

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
与模型隔离,您可以使用该策略。

© www.soinside.com 2019 - 2024. All rights reserved.