22

With SwiftUI (Xcode 11.1), I've got some Views set up with 2-way bindings (using @Binding). Two-way updating works great.

However, how can I instantiate the view from the PreviewProvider?

For example:

struct AddProjectView: View {

    @Binding public var showModal: Bool

    var body: some View {

        return VStack {
            Text("Add Project View")
            Button("Dismiss") {
                self.showModal = false
            }
        }
    }
}

I can't do this, because "true" is not a Binding:

struct AddProjectView_Previews: PreviewProvider {
    static var previews: some View {
        AddProjectView(showModal: true)
    }
}

And I can't do this because "Property wrappers are not yet supported on local properties":

struct AddProjectView_Previews: PreviewProvider {
    static var previews: some View {
        @Binding var show = true
        return AddProjectView(showModal: $show)
    }
}

How do we do this?

Thanks!!

drewster
  • 5,460
  • 5
  • 40
  • 50

3 Answers3

35

.constant is meant exactly for that:

/// Creates a binding with an immutable value.

struct AddProjectView: View {
    @Binding public var showModal: Bool
    var body: some View {
        return VStack {
            Text("Add Project View")
            Button("Dismiss") {
                self.showModal = false
            }
        }
    }
}

struct AddProjectView_Previews: PreviewProvider {
    static var previews: some View {
        AddProjectView(showModal: .constant(true))
    }
}
superpuccio
  • 11,674
  • 8
  • 65
  • 93
14

If you only need a constant value, use .constant(VALUE):

struct YourView_Previews: PreviewProvider {

    static var previews: some View {
        YourView(yourBindingVar: .constant(true))
    }

}

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 {
        BindingProvider(false) { binding in
            YourView(yourBindingVar: binding)
        }
    }

}

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

Fabian Streitel
  • 2,702
  • 26
  • 36
  • You have no idea how your answer helped me produce better code. Thanks a million. I'm still learning SWIFTUI and BindingProvider you wrote is just beyond my limited swift knowledge. I get a sense of if but dont understand 100%. Thanks Anyway. – GrandSteph Apr 27 '20 at 10:50
  • Dude this answer should be the recommended answer! – Super Noob Sep 29 '20 at 09:58
4

You have to declare it as @State on your Preview.

struct AddProjectView_Previews: PreviewProvider {

     @State static var showModal: Bool = false

     static var previews: some View {
         AddProjectView(showModal: $showModal)
     }
}

Also remeber that it needs to be static as it is used in a static func.

LuLuGaGa
  • 13,089
  • 6
  • 49
  • 57
  • 3
    The behaviour in XCode 11.3 is effectively the same as using `.constant(false)`, i.e. if you use the live preview, the value cannot be changed. – Fabian Streitel Dec 27 '19 at 15:07