SwiftUI State in Preview with PreviewProvider is not working

1.5k views Asked by At

I've a simple button that work on simulator as expected, the shape changes with click;

struct ButtonTest: View {
    @State var isLiked = true
    var body: some View {

        VStack {
            LikeButtonSimple( action: toggleLiked, isLiked: $isLiked)
        }
    }
    func toggleLiked() {
    
        isLiked.toggle()
        print("Liked Button: \(isLiked)")
    }
}

struct LikeButtonSimple: View {
    
    let dimen = 50.0
    let dimenHalf = CGFloat(28)
    
    var action: () -> Void?
    
    @Binding var isLiked : Bool

    var body: some View {
        ZStack {
            Circle()
                .frame(width: dimen, height: dimen)
                .foregroundColor(.white )
                .shadow(color: .black, radius: 2, y: 2)
            Button( action: {
               action()
            }
            ) {
                Image(systemName: isLiked ? "heart.fill" : "heart")
                    .resizable()
                    .foregroundColor(isLiked ? .red : .gray)
                    .frame(width: dimenHalf, height: dimenHalf)
            }
        }
    }
}

I want to see the same behavior on the Preview as well and wrote the below for it;

struct LikeButtonSimple_Previews: PreviewProvider {

    @State static var isLiked = true

    static func toggleLiked() {

        isLiked.toggle()
    }

    static var previews: some View {
        LikeButtonSimple( action: toggleLiked, isLiked: $isLiked)

    }
}

The button doesn't toggle anymore. Can we work this on Preview with this PreviewProvider?

3

There are 3 answers

5
Hardik Shekhat On BEST ANSWER

As Xcode Preview is static, it can't hold changed value of variable.

To change value in Preview, you will have to use the container view inside it which will holds the state value.

struct LikeButtonSimple_Previews: PreviewProvider {
    
    //A view which will wraps the actual view and holds state variable. 
    struct ContainerView: View {
        @State private var isLiked = true
        
        var body: some View {
            LikeButtonSimple(action: toggleLiked, isLiked: $isLiked)
        }
        
        func toggleLiked() {
            isLiked.toggle()
        }
    }

    static var previews: some View {
        ContainerView()
    }
} 
6
Fogmeister On

I think you don’t need the action here at all.

A binding is a two way connection to some data.

By using a binding the view that you pass it to can make updates to it.

So you do not need the bonding and the action. The button action should just do…

isLiked.toggle()

This is enough for the view to update and for the value to be updated in the “parent” view.

0
Bibin Jaimon On

This worked for me when we use SwiftUI #Preview macros.

#Preview {
    @State var isVisible: Bool = false
    return ChatView(isVisibleView: $isVisible)
}