SwiftUI onTapGesture 不适用于 Mac 应用程序中的 ObservedObject

SwiftUI onTapGesture does not work with ObservedObject in Mac app

我想根据来自另一个视图 (LeftView) 中点击手势的操作来更新视图 (RightView)。下面是我完成此操作的示例代码:

AppState.swift

import Foundation

class AppState: ObservableObject {
    @Published var tapCount: Int = 0

    func incrementTapCount() {
        self.tapCount += 1
        print("tap count \(self.tapCount)")
    }
}

LeftView.swift

import SwiftUI

struct LeftView: View {
    @ObservedObject var appState = AppState()

    var body: some View {
        VStack {
            Text("Left View")
        }
        .frame(width: 100, height: 200)
        .onTapGesture {
            print("tapped left view")
            self.appState.incrementTapCount()
        }
    }
}

RightView.swift

import SwiftUI

struct RightView: View {
    @ObservedObject var appState = AppState()

    var body: some View {
        VStack {
            Text("Right View")
            Text("Tap Count: \(self.appState.tapCount)")
        }.frame(width: 200, height: 200)
    }
}

ContentView.swift

import SwiftUI

struct ContentView: View {
    var body: some View {
        HStack {
            LeftView()
            RightView()
        }.frame(width: 300, height: 200)
    }
}

遗憾的是,RightView 文本不随点击次数更新。但是,如果我在 ObservableObject class 中使用计时器来更新点击计数,则 RightView 会正确更新。

import Foundation

class AppState: ObservableObject {
    @Published var tapCount: Int = 0

    init() {
        Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
            self.tapCount += 1
        }
    }

    func incrementTapCount() {
        self.tapCount += 1
        print("tap count \(self.tapCount)")
    }
}

为什么 LeftView 中的点击手势没有更新 RightView 文本?

LeftViewRightView 各自初始化各自的 AppState 对象,因此它们观察的状态不同。

在您的两个视图中,将 @ObservedObject 保留为未初始化的 属性:

LeftView.swift & RightView.swift

@ObservedObject var appState: AppState

当您在 SwiftUI 视图中有一个未初始化的 属性 时,您需要在任何时候想要使用该视图时为其提供一个值。

PreviewProvider 使用您的视图实例,因此 Xcode 可以在 canvas 上向您显示它,它只需要您提供一个 占位符 appState 的值。该值并不是很重要(除了 Xcode canvas 之外没有在任何地方使用它)所以您只需要提供正确类型的东西:

#if DEBUG
struct LeftView_Previews: PreviewProvider {
    static var previews: some View {
        LeftView(appState: AppState())
    }
}
#endif

最后,在 ContentView 中,您需要将对单个 shared AppState 的引用传递到 LeftViewRightView 这样他们每个人都可以观察到同一个物体:

ContentView.swift

import SwiftUI

struct ContentView: View {
    var sharedAppState = AppState()

    var body: some View {
        HStack {
            LeftView(appState: sharedAppState)
            RightView(appState: sharedAppState)
        }.frame(width: 300, height: 200)
    }
}

现在两个视图都在观察 AppState 的同一个实例,它们都会在其属性更改时更新。

您还可以使用 @EnvironmentObject 在应用中的 所有 视图之间共享状态,但由于我们在这里只关注两个视图 @ObservedObject是务实的选择。