user15936471
user15936471

Reputation:

SwiftUI - ForEach Simultaneously Toggle Multiple Buttons

I have a ForEach loop that displays ten unfilled buttons. When the button is clicked, the button assigns the index of the clicked button to a @State variable; only when the value of the state variable is equal to the index of the clicked button is the button filled. What should I change to be able to 1) click on the same button to unfill the button, and 2) fill another button without "unfilling" any already filled buttons?

Here is a minimal, reproducible example:

import SwiftUI

struct Test: View {
    @State var selection: Int? = nil
    
    var body: some View {
        VStack {
            ForEach (0 ..< 10, id: \.self) { number in
                Button (action: {
                    self.selection = number
                }, label: {
                    self.selection == number ? Image(systemName: "heart.fill") : Image(systemName: "heart")
                })
            }
        }
    }
}

struct Test_Previews: PreviewProvider {
    static var previews: some View {
        Test()
    }
}

Thanks in advance.

Upvotes: 2

Views: 2019

Answers (2)

aheze
aheze

Reputation: 30228

You need to have a selection property that can keep track of multiple buttons, not just one button. I would use a Set here (not an Array, because there's no handy remove-object method there)

struct Test: View {
    @State var selection = Set<Int>() /// will contain the indices of all selected buttons
    
    var body: some View {
        VStack {
            ForEach (0 ..< 10, id: \.self) { number in
                Button(action: {
                    if selection.contains(number) {
                        selection.remove(number)
                    } else {
                        selection.insert(number)
                    }
                }) {
                    selection.contains(number) ? Image(systemName: "heart.fill") : Image(systemName: "heart")
                }
            }
        }
    }
}

Upvotes: 4

Tushar Sharma
Tushar Sharma

Reputation: 2882

You have to maintain state separately for each button.Better option is to use array. Check code below.

import SwiftUI

struct Test: View {
    @State var selection: [Int] = Array(repeating:-1,count:10)
    
    var body: some View {
        VStack {
            ForEach (0..<selection.count, id: \.self) { index in
                Button (action: {
                    if self.selection[index] == index {
                        self.selection[index] = -1
                    }else{
                        self.selection[index] = index
                    }
                    
                }, label: {
                    self.selection[index] == index ? Image(systemName: "heart.fill") : Image(systemName: "heart")
                })
            }
        }
    }
}

struct Test_Previews: PreviewProvider {
    static var previews: some View {
        Test()
    }
}

Upvotes: 0

Related Questions