skip to Main Content

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.

2

Answers


  1. 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()
        }
    }
    
    Login or Signup to reply.
  2. 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")
                    }
                }
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search