Vlad the Impala
Vlad the Impala

Reputation: 15872

Is there something like @ObservedObject that doesn't involve creating a new class?

I have a problem like this one, but @Binding doesn't seem like a good solution for it. I have a view that has child views, which have their own child views, and so on. They all have a label attribute. If I click a button, I want to set the labels for all the views down the whole tree. The code I have is something like this:

@State var label:String = ""

func setLabel(l:String) {
    label = l
    for c in children {
        c.setLabel(l)
    }
}

This doesn't work, but if I change label to an ObservedObject, it works:

class Label: ObservableObject {
    @Published var label:String = ""
    
    func setLabel(_ s:String) {
        label = s
    }
}

@ObservedObject var label:Label = Label()

I don't want to create a new wrapper class each time I have to do this, though. Is there a different way?

Upvotes: 0

Views: 37

Answers (1)

Asperi
Asperi

Reputation: 257779

Single source of truth, in this scenario we need only one @State, and then all hierarchy will be updated correctly...

Here is a demo. Prepared with Xcode 12.1 / iOS 14.1

demo

struct ContentView: View {
    @State private var label = "-1"
    var body: some View {
        VStack {
            Button("Update") { self.label = "\(Int.random(in: 0...9))" }
            Text("Main: \(label)")
            ChildDemoView(label: label)
        }
    }
}

struct ChildDemoView: View {
    let label: String
    var level = 1
    var body: some View {
        VStack {
            Text("Child\(level): \(label)")
            if level < 10 {
                ChildDemoView(label: label, level: level + 1)
            }
        }
    }
}

Upvotes: 1

Related Questions