为什么在Swift中绑定CoreData的NSManagedObject时,onChange不会更新?

huangapple go评论65阅读模式
英文:

Why does Binding of CoreData NSManagedObject not update onChange in Swift?

问题

问题的原因是在DetailView中,本地的Toggle状态改变时,未能正确地触发entity.enabled的绑定。解决方法是确保在localEnabled改变时,手动更新绑定的entity.enabled。可以尝试在DetailViewonChange闭包中,显式地将entity.enabled的值设置为localEnabled

.onChange(of: localEnabled) { newValue in
    entity.enabled = newValue
}
英文:

Context

I have a pretty simple Form using the MVVM architecture. However, I encountered a problem with this implementation. My DetailView has a DummyState that changes the corresponding attribute of Entity onChange.

> Problem: Once the DetailView appears, both Toggles are on which is as expected. However, when turning the local Toggle off, the other Toggle remains on, which is unexpected. However, as soon as I interact with another element (not in MRE), the second Toggle seems to refresh and turns off.


Code

@objc(Entity) public class Entity: NSManagedObject {
    @NSManaged public var enabled: Bool
}

class FormViewModel: ObservableObject, CustomFormObservable {
    @Published var entity = Entity(context: ...)

    init() { self.entity.enabled = true }

    var isValid: Bool { self.entity.enabled }
}

struct FormView: View {
    @StateObject private var formVM = FormViewModel()

    var body: some View {
        CustomForm(formVM: formVM) {
            DetailView(entity: $formVM.entity)
        }
    }
}

struct DetailView: View {
    @Binding var entity: Entity
    @State private var localEnabled: Bool = true

    var body: some View {
        Toggle("Local Toggle", isOn: $localEnabled)
            .onChange(of: localEnabled) { entity.enabled = $0 }

        Toggle("Toggle", isOn: $entity.enabled)
    }
}

Question

  • What causes this behaviour and how can I solve it?

答案1

得分: 1

因为 NSManagedObject 是引用类型。

有一个简单的规则:

  • @State@Binding 用于值类型,如结构体。
  • @StateObject@ObservedObject 用于引用类型,如类。

而且 美元符号引用 也仅用于值类型。

幸运的是,NSManagedObject 默认符合 ObservableObject


struct MainView: View {
    @StateObject private var formVM = FormViewModel()

    var body: some View {
        DetailView(entity: formVM.entity)
    }
}

struct DetailView: View {
    @ObservedObject var entity: Entity
    @State private var localEnabled: Bool = true

    var body: some View {
        Toggle("本地切换", isOn: $localEnabled)
            .onChange(of: localEnabled) { entity.enabled = $0 }

        Toggle("切换", isOn: $entity.enabled)
    }
}
英文:

Because NSManagedObject is reference type.

There is a simple rule:

  • @State and @Binding is for value types like structs.
  • @StateObject and @ObservedObject is for reference types like classes.

And the dollar-sign reference is also only for value types.

Fortunately NSManagedObject conforms to ObservableObjectby default.


struct MainView: View {
    @StateObject private var formVM = FormViewModel()

    var body: some View {
        DetailView(entity: formVM.entity)
    }
}

struct DetailView: View {
    @ObservedObject var entity: Entity
    @State private var localEnabled: Bool = true

    var body: some View {
        Toggle("Local Toggle", isOn: $localEnabled)
            .onChange(of: localEnabled) { entity.enabled = $0 }

        Toggle("Toggle", isOn: $entity.enabled)
    }
}

huangapple
  • 本文由 发表于 2023年5月20日 23:14:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/76295924.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定