Weird @Binding behavior, doesn't update the value until I set it into different one

47 Views Asked by At

So I have this simple test code to test how I can communicate between two views using @Binding. It's supposed to be simple in which there's a list of buttons that when it's pressed it'll open a simple view showing which value they pick.

struct SimpleButtonView: View {
    @Binding var bindingValueBool: Bool
    @Binding var bindingValueInt: Int
    var myNumber: Int
 
    var body: some View {
        Button(action: {
            // Change the values
            bindingValueBool = true
            bindingValueInt = myNumber
        }, label: {
            Text("Press me \(myNumber)")
        })
    }
}

struct TestView: View {
    @State var bindingValueBool: Bool = false
    @State var bindingValueInt: Int = 0
 
    var body: some View {
        ForEach(1 ... 10, id: \.self) {
            SimpleButtonView(bindingValueBool: $bindingValueBool, bindingValueInt: $bindingValueInt, myNumber: $0)
        }.sheet(isPresented: $bindingValueBool, content: {
            // This should show the number selected?
            // How come it's sometimes correct sometimes shows default value 0 as if the bindingValueInt hasn't got the updated value yet
            Text("This is the selected number \(bindingValueInt)")
        })
    }
}

Pretty straightfoward right? I thought I did it according to what I understood about properties modifier and swiftui, but the result is weird. It seems bindingValueInt doesn't update properly ?

I need to click two different buttons to make sure it works, as if the first click on the button and the first update to those binding properties doesn't get propagated to the main view?

Which is weird because bindingValueBool is always changed as it never fails to show the new sheet? It's just the other binding property that somehow stays to its default until I click "a different" button?

Can someone help me to understand this?

1

There are 1 best solutions below

0
Benzy Neez On

A more reliable way to do it is to use the alternative .sheet modifier that takes an optional Identifiable item, because then you only need to update one binding instead of two and the value is delivered to the function that launches the sheet:

struct IdentifiableInt: Identifiable {
    let val: Int
    var id: Int { val }
}

struct SimpleButtonView: View {
    @Binding var bindingValueInt: IdentifiableInt?
    var myNumber:Int

    var body: some View {
        Button(action: {
            //Change the value
            bindingValueInt = IdentifiableInt(val: myNumber)
        }, label: {
            Text("Press me \(myNumber)")
        })
    }
}

struct TestView: View {
    @State var bindingValueInt: IdentifiableInt? = nil

    var body: some View {
        ForEach(1...10, id: \.self){
            SimpleButtonView(bindingValueInt: $bindingValueInt, myNumber: $0)
        }
        .sheet(item: $bindingValueInt) { item in
            Text("This is the selected number \(item.val)")
        }
    }
}