de.
de.

Reputation: 8427

How to create a SwiftUI #Preview in Xcode 15 for a view with a @Binding

If I wanted to create a preview for a SwiftUI view that contains a @Binding I would have previously written something like this:

struct SpecialButton_Preview: PreviewProvider {
    static var previews: some View {
        @State var value: Bool = true
        SpecialButton(isOn: $value)
    }
}

However Xcode 15 now comes with a new syntax (#Preview) but when I try to add my example state property, it does not work:

#Preview {  // Error: Ambiguous use of 'Preview(_:traits:body:)'
    @State var value: Bool = true
    SpecialButton(isOn: $value)
}

How can I make this work?

Upvotes: 39

Views: 25592

Answers (8)

Thiago Sena
Thiago Sena

Reputation: 1

I was having the same problem. Instead it is not like before, the previews exist yet. To do so, just create new previews and they will appear on top area on canvas windows. Like this code:

struct _1_EnvironmentObject_Previews: PreviewProvider {
    static var previews: some View {
        _1_EnvironmentObject()
    }
}

struct DetailView_Previews: PreviewProvider {
    static var previews: some View {
        DetailView(selectedItem: "iPhone")
    }
}

Screenshot 1st preview:

Screenshot 1st preview

Screenshot 2nd preview:

Screenshot 2nd preview

Upvotes: 0

Alexander Sandberg
Alexander Sandberg

Reputation: 1873

Xcode 16+

You can now achieve this using the new @Previewable:

#Preview {
    @Previewable @State var value = true
    SpecialButton(isOn: $value)
}

Upvotes: 5

Mikaela Caron
Mikaela Caron

Reputation: 727

You need to return the View to preview. I'm not exactly sure how this works, it has to do with how Swift macros work.

#Preview {
    @State var value: Bool = true
    return SpecialButton(isOn: $value)
}

From the WWDC Slack: "The new #Previews macro simply takes a closure that returns the thing to be previewed. So you can declare local variables or do other setup necessary in there just like you would in any other closure."

Edit: This only works when you don't want to update the state, otherwise you have to follow the way mentioned by de.

Updated for Xcode 16:

New @Previewable macro for using dynamic properties inline in #Preview

#Preview {
    @Previewable @State var value = true
    SpecialButton(isOn: $value)
}

Upvotes: 60

MJFree34
MJFree34

Reputation: 21

I personally have a reusable view that I use to allow for the @Binding to be manipulated in the Live Preview. It looks like the following:

public struct PreviewBindingWrapper<T, Content: View>: View {
    @State private var wrappedBinding: T
    private let content: (Binding<T>) -> Content

    public init(wrappedBinding: T, @ViewBuilder content: @escaping (Binding<T>) -> Content) {
        self._wrappedBinding = State(wrappedValue: wrappedBinding)
        self.content = content
    }

    public var body: some View {
        content($wrappedBinding)
    }
}

This can then be used in your preview like:

#Preview {
    PreviewBindingWrapper(true) { booleanBinding in
        SpecialButton(isOn: booleanBinding)
    }
}

Hope this helps!

Upvotes: 1

David
David

Reputation: 311

Thanks - I have a similar error: Ambiguous use of 'init(_:traits:body:)'

#Preview {
    NewPositionView(symbol: .constant("AAPL") )
}

& with a @State var without the return inside of Preview Error: Result of 'NewPositionView' initializer is unused

This fixed it... adding the return inside #Preview

#Preview {
    @State var sym: String = "AAPL"
    return NewPositionView(symbol: $sym )
}

Upvotes: 3

Interior Night
Interior Night

Reputation: 1225

Do you need to keep it as a binding var in your preview? Otherwise try this, as it seems to work for me:

#Preview {
    SpecialButton(isOn: true)
}

I'd elaborate on the 'why' but that's still unclear!

Upvotes: -5

de.
de.

Reputation: 8427

This is what I ended up doing in order to have a mutable value:


#Preview {
    struct PreviewWrapper: View {
        @State var value: Bool = true
        
        var body: some View {
            SpecialButton(isOn: $value)
        }
    }
    return PreviewWrapper()
}

Upvotes: 42

ChrisR
ChrisR

Reputation: 12105

What you could do before and still can do is:

SpecialButton(isOn: .constant(true))

Upvotes: 8

Related Questions