iosswiftui

Why @State property does not keep state while not using it make the app working properly?


Just simple case:

import SwiftUI

struct TestView: View {
    @State var elements = ["A", "B", "C", "D", "E", "F"]
    @State var selectedElement: String?
    var body: some View {
        VStack(spacing: 8) {
            ForEach(elements, id: \.self) { element in
                Button {
                    withAnimation(.easeInOut(duration: 0.5)) {
                        if selectedElement == element {
                            selectedElement = nil
                        } else {
                            selectedElement = element
                        }
                    }
                } label: {
                    ElementView(element: element, isSelected: selectedElement == element)
                }
                if selectedElement == element {
                    Text("selected")
                }
            }
            Spacer()
                .frame(minHeight: 90)
        }
    }
}


struct ElementView: View {
    @State var element: String
    var isSelected: Bool // HERE @State ❌, no @State ✅
    var body: some View {
        Text(element)
            .foregroundStyle(isSelected ? .red : .blue)
    }
}


#Preview {
    TestView()
}

Outputs:

@State var isSelected: Bool

enter image description here

var isSelected: Bool

enter image description here

Why it does not work with @State?


Solution

  • You use @State where a value is owned by the view.

    You use @Binding to receive an @State property that is owned by some other view.

    In this case isSelected is not state owned by ElementView, so you do not use @State.
    It is not @State owned by TestView, so you don't use @Binding either.

    It is simply a property passed to the view from the superview.

    Because the element that is passed to ElementView is @State that is owned by TestView, you should receive it as an @Binding

    struct ElementView: View {
        @Binding var element: String
        let isSelected: Bool // HERE @State ❌, no @State ✅
        var body: some View {
            Text(element)
                .foregroundStyle(isSelected ? .red : .blue)
        }
    }