17

I have a ChildView with a variable:

@Binding var itemName: String

In this ChildView I have few buttons that change value of the variable:

Button(action: {
    self.itemName = "different value"
})

I was trying to use Preview like this:

struct ChildView_Previews: PreviewProvider {
    static var previews: some View {
        ChildView(itemName: "test")
    }
}

But I am getting an error:

Cannot convert value of type 'String' to expected argument type 'Binding'

I am aware that I can use Preview like below. And the error will be gone and preview will work, but... itemName will have constant value, it will not be mutable now, not interactive in Live Preview:

struct ChildView_Previews: PreviewProvider {
    static var previews: some View {
        ChildView(itemName: .constant("test"))
    }
}

How to declare a binding in SwiftUI Preview to make it interactive?

2 Answers 2

20

Updates to a @State variable in a PreviewProvider appear to not update the the read-only computed property previews directly. The solution is to wrap the @State variable in a test holder view. Then use this test view inside the previews property so the Live Preview refreshes correctly. Tested and working in Xcode 11.2.1.

struct ChildView: View {
    @Binding var itemName: String

    var body: some View {
        VStack {
            Text("Name: \(itemName)")
            Button(action: {
                self.itemName = "different value"
            }) {
                Text("Change")
            }
        }
    }
}

struct ChildView_Previews: PreviewProvider {

    struct BindingTestHolder: View {
        @State var testItem: String = "Initial"
        var body: some View {
            ChildView(itemName: $testItem)
        }
    }

    static var previews: some View {
        BindingTestHolder()
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, Asperi, but it doesn't work. There is no error, but itemName in Live Preview is still not interactive. I think it's because of @Binding var itemName: String. I have also updated my question to show how I am using Buttons to change value of itemName.
I believed it worked before... now looks like @State does not invalidate previews anymore. Updated.
9

If you need a value that can be changed in the live preview, I like to use this helper class:

struct BindingProvider<StateT, Content: View>: View {
    
    @State private var state: StateT
    private var content: (_ binding: Binding<StateT>) -> Content
    
    init(_ initialState: StateT, @ViewBuilder content: @escaping (_ binding: Binding<StateT>) -> Content) {
        self.content = content
        self._state = State(initialValue: initialState)
    }
    
    var body: some View {
        self.content($state)
    }
}

Use it like so:

struct YourView_Previews: PreviewProvider {
    
    static var previews: some View {
        var yourVar = "example"
        BindingProvider(yourVar) { binding in
            YourView(initVar: binding)
        }
    }

}

This allows you to test changing the binding in the live preview.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.