Papi
Papi

Reputation: 345

Swift state for automatically generated views

I am wondering how I can set a state for each generated view (looping through a ForEach).

For example, I would like to loop over this list and have a selection variable for each one (circle is outlined when not selected and filled when selected).

I tried making a local variable, but that doesn't change when a button is pressed because the view is already rendered. I tried adding a "selected variable" to the data model but for some reason it keeps saying I can't change the value because it is a "let constant" even though I created it with a "var".

ForEach(recipe.ingredients, id: \.self) { ingredient in
    var selected: Bool = false
    Button {
        selected = true
        print("selected")
    } label: {
        HStack {
            if selected {
                Image(systemName: "circle")
                    .imageScale(.large)
                    .foregroundColor(Color.pink)
            } else {
                Image(systemName: "circle.fill")
                    .imageScale(.large)
                    .foregroundColor(Color.pink)
            }
        }
    }
}

Here is the model for the data

struct Response: Decodable {
    let data: [Data]
}

struct Data: Decodable, Hashable {
    let name: String
    let image: String
    let ingredients: [String]
    let instructions: [String]
    let servings: String
    let time: [String: String]
}

I tried doing the following but it says "selected" is a let constant.

struct Response: Decodable {
    let data: [Data]
}

struct Data: Decodable, Hashable {
    let name: String
    let image: String
    var ingredients: [Ingredients]
    let instructions: [String]
    let servings: String
    let time: [String: String]
}

struct Instructions: Decodable, Hashable {
    var selected: Bool = false
    let instructions: String
}

Upvotes: 0

Views: 43

Answers (1)

Papi
Papi

Reputation: 345

I found a solution. By putting each generated view (from ForEach) into it's own view with it's own state and passing the ingredient down to the other view.

ForEach(recipe.ingredients, id: \.self) { ingredient in
    IngredientView(ingredient: ingredient)
}

IngredientView:

struct IngredientView: View {
    @State private var selected: Bool = false
    var ingredient: String
    
    var body: some View {
        Button {
            selected.toggle()
        } label: {
            HStack {
                if !selected {
                    Image(systemName: "circle")
                        .imageScale(.large)
                        .foregroundColor(Color.pink)
                } else {
                    Image(systemName: "circle.fill")
                        .imageScale(.large)
                        .foregroundColor(Color.pink)
                }
            }
        }
        .buttonStyle(.plain)
    }
}

Upvotes: 1

Related Questions