Reputation: 8427
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
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")
}
}
Upvotes: 0
Reputation: 1873
You can now achieve this using the new @Previewable
:
#Preview {
@Previewable @State var value = true
SpecialButton(isOn: $value)
}
Upvotes: 5
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.
New @Previewable
macro for using dynamic properties inline in #Preview
#Preview {
@Previewable @State var value = true
SpecialButton(isOn: $value)
}
Upvotes: 60
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
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
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
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
Reputation: 12105
What you could do before and still can do is:
SpecialButton(isOn: .constant(true))
Upvotes: 8