gpt4 book ai didi

swift - 如何在以下 mvvm 架构中使用 @Binding Wrapper?

转载 作者:行者123 更新时间:2023-12-03 10:15:33 26 4
gpt4 key购买 nike

我已经建立了一个 mvvm 架构。我有一个模型,一堆 View ,每个 View 都有一个商店。为了说明我的问题,请考虑以下内容:
在我的模型中,存在一个用户对象 user和两个 View (A 和 B)和两个商店(商店 A,商店 B),它们都使用用户对象。 View A 和 View B 不相互依赖(两者都有不共享用户对象的不同存储)但都能够编辑 user 的状态目的。显然,您需要以某种方式将更改从一个商店传播到另一个商店。为了做到这一点,我构建了一个存储层次结构,其中一个根存储维护整个“应用程序状态”(共享对象的所有状态,如 user )。现在,Store A 和 B 只维护对根存储对象的引用,而不是维护对象本身。我现在预计,如果我更改 View A 中的对象,存储 A 会将更改传播到根存储,根存储会再次将更改传播到存储 B。当我切换到 View B 时,我应该能够现在看看我的变化。我使用 Store A 和 B 中的 Bindings 来引用根存储对象。但这不能正常工作,我只是不理解 Swift 绑定(bind)的行为。这是我作为简约版本的具体设置:

public class RootStore: ObservableObject {
@Published var storeA: StoreA?
@Published var storeB: StoreB?

@Published var user: User

init(user: User) {
self.user = user
}
}

extension ObservableObject {
func binding<T>(for keyPath: ReferenceWritableKeyPath<Self, T>) -> Binding<T> {
Binding(get: { [unowned self] in self[keyPath: keyPath] },
set: { [unowned self] in self[keyPath: keyPath] = $0 })
}
}

public class StoreA: ObservableObject {
@Binding var user: User

init(user: Binding<User>) {
_user = user
}
}

public class StoreB: ObservableObject {
@Binding var user: User

init(user: Binding<User>) {
_user = user
}
}
在我的 SceneDelegate.swift 中,我有以下片段:
    user = User()
let rootStore = RootStore(user: user)
let storeA = StoreA(user: rootStore.binding(for: \.user))
let storeB = StoreB(user: rootStore.binding(for: \.user))

rootStore.storeA = storeA
rootStore.storeB = storeB

let contentView = ContentView()
.environmentObject(appState) // this is used for a tabView. You can safely ignore this for this question
.environmentObject(rootStore)
然后,contentView 作为 rootView 传递给 UIHostingController。现在我的内容 View :
struct ContentView: View {
@EnvironmentObject var appState: AppState
@EnvironmentObject var rootStore: RootStore

var body: some View {
TabView(selection: $appState.selectedTab) {
ViewA().environmentObject(rootStore.storeA!).tabItem {
Image(systemName: "location.circle.fill")
Text("ViewA")
}.tag(Tab.viewA)

ViewB().environmentObject(rootStore.storeB!).tabItem {
Image(systemName: "waveform.path.ecg")
Text("ViewB")
}.tag(Tab.viewB)
}
}
}
现在,两种观点:
struct ViewA: View {
// The profileStore manages user related data
@EnvironmentObject var storeA: StoreA

var body: some View {
Section(header: HStack {
Text("Personal Information")
Spacer()
Image(systemName: "info.circle")
}) {
TextField("First name", text: $storeA.user.firstname)
}
}
}

struct ViewB: View {
@EnvironmentObject var storeB: StoreB

var body: some View {
Text("\(storeB.user.firstname)")
}
}
最后,我的问题是,变化并没有像他们应该的那样反射(reflect)。当我在 ViewA 中更改某些内容并切换到 ViewB 时,我看不到更新后的用户名。当我改回 ViewA 时,我的更改也丢失了。我用了 didSet在商店内部和类似的用于调试目的和绑定(bind)实际上似乎工作。更改被传播但不知何故 View 只是不更新​​。我还强制进行了一些人为的状态更改(添加一个状态 bool 变量并在 onAppear() 中切换它) View 重新呈现,但它仍然没有采用更新的值,我只是不知道该怎么做。
编辑:这是我的用户对象的最小版本
public struct User {
public var id: UUID?
public var firstname: String
public var birthday: Date

public init(id: UUID? = nil,
firstname: String,
birthday: Date? = nil) {
self.id = id
self.firstname = firstname
self.birthday = birthday ?? Date()
}
}
为简单起见,我没有传递上面 SceneDelegate.swift 片段中的属性。

最佳答案

在您的场景中,将 User 作为 ObservableObject 并在存储之间通过引用传递它,以及在相应的 View 中显式用作 ObservedObject 更为合适。
这是从您的代码快照组合并应用了该想法的简化演示。
使用 Xcode 11.4/iOS 13.4 测试
enter image description here

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let user = User(id: UUID(), firstname: "John")
let rootStore = RootStore(user: user)
let storeA = StoreA(user: user)
let storeB = StoreB(user: user)
rootStore.storeA = storeA
rootStore.storeB = storeB

return ContentView().environmentObject(rootStore)
}
}

public class User: ObservableObject {
public var id: UUID?
@Published public var firstname: String
@Published public var birthday: Date

public init(id: UUID? = nil,
firstname: String,
birthday: Date? = nil) {
self.id = id
self.firstname = firstname
self.birthday = birthday ?? Date()
}
}

public class RootStore: ObservableObject {
@Published var storeA: StoreA?
@Published var storeB: StoreB?

@Published var user: User

init(user: User) {
self.user = user
}
}

public class StoreA: ObservableObject {
@Published var user: User

init(user: User) {
self.user = user
}
}

public class StoreB: ObservableObject {
@Published var user: User

init(user: User) {
self.user = user
}
}

struct ContentView: View {
@EnvironmentObject var rootStore: RootStore

var body: some View {
TabView {
ViewA(user: rootStore.user).environmentObject(rootStore.storeA!).tabItem {
Image(systemName: "location.circle.fill")
Text("ViewA")
}.tag(1)

ViewB(user: rootStore.user).environmentObject(rootStore.storeB!).tabItem {
Image(systemName: "waveform.path.ecg")
Text("ViewB")
}.tag(2)
}
}
}

struct ViewA: View {
@EnvironmentObject var storeA: StoreA // keep only if it is needed in real view

@ObservedObject var user: User
init(user: User) {
self.user = user
}

var body: some View {
VStack {
HStack {
Text("Personal Information")
Image(systemName: "info.circle")
}
TextField("First name", text: $user.firstname)
}
}
}

struct ViewB: View {
@EnvironmentObject var storeB: StoreB

@ObservedObject var user: User
init(user: User) {
self.user = user
}

var body: some View {
Text("\(user.firstname)")
}
}

关于swift - 如何在以下 mvvm 架构中使用 @Binding Wrapper?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62655457/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com