53

How do I generate a preview provider for a view which has a binding property?

struct AddContainer: View {
    @Binding var isShowingAddContainer: Bool
    var body: some View {
        Button(action: {
                self.isShowingAddContainer = false
            }) {
                Text("Pop")
            }
    }
}

struct AddContainer_Previews: PreviewProvider {
    static var previews: some View {
        // ERROR HERE <<<-----
        AddContainer(isShowingAddContainer: Binding<Bool>()
    }
}

In Code above, How to pass a Binding<Bool> property in an initialiser of a view?

  • Please check or review solution on the below link : https://stackoverflow.com/questions/56685964/swiftui-binding-initialize – Rock Feb 07 '20 at 03:13

3 Answers3

97

Just create a local static var, mark it as @State and pass it as a Binding $

struct AddContainer_Previews: PreviewProvider {
  @State static var isShowing = false
  static var previews: some View {
    AddContainer(isShowingAddContainer: $isShowing)
  }
}
nine stones
  • 3,264
  • 1
  • 24
  • 36
  • 2
    Thanks that worked! But why do I need static here and not in my main view ? This is still puzzling me – tbrodbeck Nov 20 '20 at 21:28
  • 7
    Because the PreviewProvider protocol is a static protocol. From Apple docs: "Xcode statically discovers types that conform to the PreviewProvider protocol in your app, and generates previews for each provider it discovers." The static var `previews` can only access static variables. – nine stones Nov 21 '20 at 16:26
36

If you want to watch the binding:

Both other solutions [the "static var" variant AND the "constant(.false)"-variant work for just seeing a preview that is static. But you cannot not see/watch the changes of the value intended by the button action, because you get only a static preview with this solutions.

If you want to really watch (in the life preview) this changing, you could easily implement a nested view within the PreviewProvider, to - let's say - simulate the binding over two places (in one preview).

import SwiftUI

struct BoolButtonView: View {
    @Binding var boolValue : Bool
    var body: some View {
        VStack {
            Text("The boolValue in BoolButtonView = \(boolValue.string)")
                .multilineTextAlignment(.center)
                .padding()
            Button("Toggle me") {
                boolValue.toggle()
            }
            .padding()
        }
    }
}

struct BoolButtonView_Previews: PreviewProvider {
    
    // we show the simulated view, not the BoolButtonView itself
    static var previews: some View {
        OtherView()
            .preferredColorScheme(.dark)
    }
    
    // nested OTHER VIEW providing the one value for binding makes the trick
    private struct OtherView : View {
        
        @State var providedValue : Bool = false
        
        var body: some View {
            BoolButtonView(boolValue: $providedValue)
        }
    }
}
LukeSideWalker
  • 7,399
  • 2
  • 37
  • 45
  • Yes, this is the answer, unfortunaly since XCode 13, the debugging is removed and the print statements are not visible in the preview. I've tries to attach the debugger to the preview and it is not worked. – kelalaka Apr 07 '23 at 07:03
  • I've noticed that, if you make a separate view instead of nested, the print statements are working. [Can someone verify this?](https://stackoverflow.com/a/76185165/1820553) – kelalaka May 23 '23 at 16:14
  • 1
    Add *private* in front on struct OtherView: View to keep it hidden from rest of your code : _private struct OtherView : View_ – JeanNicolas Jun 09 '23 at 06:22
  • This doesn't work in XCode 14: _"Compiling failed: main actor-isolated static property 'previews' can not be referenced from a non-isolated context"_ You need to add `@MainActor` to the `OtherView` struct, then it compiles. – iSpain17 Aug 28 '23 at 17:53
26

Other way

    struct AddContainer_Previews: PreviewProvider {
      static var previews: some View {
        AddContainer(isShowingAddContainer: .constant(false))
      }
    }
Ernist Isabekov
  • 1,205
  • 13
  • 20
  • 1
    While the `.constant(false)` binding in most cases will be sufficient: A variable `@State` offers you the possibility to add a button to the preview changing the value in the preview in run-mode. This way you can see the effect of your `@State/@Binding` directly in the preview in run mode – nine stones Feb 07 '20 at 17:11