1

I'm new to programming and SwiftUI and I am making this app where users can select these buttons labeled A-D. They may choose more than 1, and I am hoping that when they click on the button, the background colour will change from grey to green. However, if I replace "// Here" in the code at the bottom with

Data.Selected = true
Data.Colour = .green

I get an error saying "Cannot assign to property: 'Data' is a 'let' constant". I understand what that means, but I don't know how to change Data to var. I tried typing var in front of "Data in" but I got this error instead "Consecutive statements on a line must be separated by ';'". Is there anyway I can directly modify Data/ButtonsData? Or is there a workaround?

struct Buttons: Hashable {
    var Crit: String
    var Selected: Bool
    var Colour: Color
}

var ButtonsData = [
    Buttons(Crit: "A", Selected: false, Colour: Color(.systemGray4)),
    Buttons(Crit: "B", Selected: false, Colour: Color(.systemGray4)),
    Buttons(Crit: "C", Selected: false, Colour: Color(.systemGray4)),
    Buttons(Crit: "D", Selected: false, Colour: Color(.systemGray4))
]

struct CritView: View {
    
    @Binding var CritBoard: Bool
    @Binding var BackgroundColor: Color
    
    var body: some View {
        ZStack(alignment: .topLeading) {
            
            ScrollView(.vertical, showsIndicators: false) {
                HStack(spacing: 15) {
                    ForEach(ButtonsData, id: \.self) { Data in
                        Button(action: {
                          // HERE
                        }) {
                            Text(Data.Crit)
                                .font(.system(size: 30))
                        }
                        .frame(width: 65, height: 55)
                        .background(Data.Colour)
                        .cornerRadius(10)
                    }
                }
                .padding(.top, 50)
            }
            .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height/8)
            .padding(.bottom, UIApplication.shared.windows.first?.safeAreaInsets.bottom)
            .background(Color(.white))
            .cornerRadius(25)
            
            Button(action: {
                self.CritBoard.toggle()
                self.BackgroundColor = .white
            }) {
                Image(systemName: "xmark").foregroundColor(.black)
            }.padding(25)
        }
    }
}

2 Answers 2

3

Here is possible solution - create array of index/value tuples and modify your data in original container by index:

ForEach(Array(ButtonsData.enumerated()), id: \.element) { i, Data in
    Button(action: {
      ButtonsData[i].Selected = true
      ButtonsData[i].Colour = .green
    }) {
        Text(Data.Crit)
            .font(.system(size: 30))
    }
    .frame(width: 65, height: 55)
    .background(Data.Colour)
    .cornerRadius(10)
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your answer! After copying and pasting your code, there were not any errors, but unfortunately did not work. The problem was that while the "ButtonsData[i].Colour" was indeed changed, ".background(ButtonsData[I].Colour)" did not since it wasn't an @State variable. I did a bit of searching, and found this Hacking with Swift article helpful (hackingwithswift.com/books/ios-swiftui/…). I therefore added "@State private var AllData = ButtonsData" and changed "ButtonsData[I].Colour" with "self.AllData[i].Colour".
2

Well, I don't think a lot of people would actually have the same/similar problem but this is my working code. The code uses both @Aspersi's answer as well as the code in a Hacking with Swift article. (it might not be the most simplified code, but it works right now at least)

ForEach(Array(ButtonsData.enumerated()), id: \.element) { i, Data in
    Button(action: {
        self.AllData[i].Selected.toggle()
        if self.AllData[i].Selected == true {
            self.AllData[i].Colour = .green
        } else {
            self.AllData[i].Colour = Color(.systemGray4)
        }
    }) {
        Text(Data.Crit)
            .font(.system(size: 30))
    }
    .frame(width: 65, height: 55)
    .background(self.AllData[i].Colour)
    .cornerRadius(10)
}

Full code below

struct Buttons: Hashable {
    var Crit: String
    var Selected: Bool = false
    var Colour: Color
}

var ButtonsData = [
    Buttons(Crit: "A", Selected: false, Colour: Color(.systemGray4)),
    Buttons(Crit: "B", Selected: false, Colour: Color(.systemGray4)),
    Buttons(Crit: "C", Selected: false, Colour: Color(.systemGray4)),
    Buttons(Crit: "D", Selected: false, Colour: Color(.systemGray4))
]

struct CritView: View {
    
    @Binding var CritBoard: Bool
    @Binding var BackgroundColor: Color
    
    @State private var AllData = ButtonsData
    
    var body: some View {
        ZStack(alignment: .topLeading) {
            
            ScrollView(.vertical, showsIndicators: false) {
                HStack(spacing: 15) {
                    ForEach(Array(ButtonsData.enumerated()), id: \.element) { I, Data in
                        Button(action: {
                            self.AllData[i].Selected.toggle()
                            if self.AllData[i].Selected == true {
                                self.AllData[i].Colour = .green
                            } else {
                                self.AllData[i].Colour = Color(.systemGray4)
                            }
                        }) {
                            Text(Data.Crit)
                                .font(.system(size: 30))
                        }
                        .frame(width: 65, height: 55)
                        .background(self.AllData[i].Colour)
                        .cornerRadius(10)
                    }
                }
                .padding(.top, 50)
            }
            .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height/8)
            .padding(.bottom, UIApplication.shared.windows.first?.safeAreaInsets.bottom)
            .background(Color(.white))
            .cornerRadius(25)
            
            Button(action: {
                self.CritBoard.toggle()
                self.BackgroundColor = .white
            }) {
                Image(systemName: "xmark").foregroundColor(.black)
            }.padding(25)
        }
    }
}

1 Comment

How do I deselect all other buttons

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.