Search code examples
swiftuigridview

Change background color of a shape in a grid


I made a grid using LazyGrid and I want to change individual item background color when it tapped based on its current selection status in the data model.

I tried to use onTapGesture() it doesn't reload the grid and update the background color

class Day: Identifiable {
    let id = UUID()
    let value: Int
    var isSelected: Bool
    
    init(value: Int, isSelected: Bool) {
        self.value = value
        self.isSelected = isSelected
    }
}

import SwiftUI

struct ContentView: View {
    var days = [
        Day(value: 1, isSelected: true),
        Day(value: 2, isSelected: false),
        Day(value: 3, isSelected: false),
        Day(value: 4, isSelected: true)
    ]
    
    let layout = [
           GridItem(.fixed(40)),
           GridItem(.fixed(40)),
           GridItem(.fixed(40)),
           GridItem(.fixed(40))
    ]
    
    var body: some View {
        ScrollView {
            LazyVGrid(columns: layout) {
                ForEach(days) { day in
                    Capsule()
                        .overlay(Text("\(day.value)").foregroundColor(.white))
                        .foregroundColor(day.isSelected ? .blue : .red)
                        .frame(height: 40)
                        .onTapGesture {
                            days[0].isSelected.toggle()
                        }
                }
            }
        }
    }
}

Solution

  • Try this approach, as mentioned using a struct Day and enumerated in the ForEach loop, and @State private var days ... as shown in the example code:

    struct Day: Identifiable {  // <--- here
        let id = UUID()
        let value: Int
        var isSelected: Bool
        
        init(value: Int, isSelected: Bool) {
            self.value = value
            self.isSelected = isSelected
        }
    }
    
    
    struct ContentView: View {
        
        @State private var days = [         // <--- here
            Day(value: 1, isSelected: true),
            Day(value: 2, isSelected: false),
            Day(value: 3, isSelected: false),
            Day(value: 4, isSelected: true)
        ]
        
        let layout = [
            GridItem(.fixed(40)),
            GridItem(.fixed(40)),
            GridItem(.fixed(40)),
            GridItem(.fixed(40))
        ]
        
        var body: some View {
            ScrollView {
                LazyVGrid(columns: layout) {
    
                    // --- here
                    ForEach(Array(days.enumerated()), id: \.offset) { index, day in 
                        Capsule()
                            .overlay(Text("\(day.value)").foregroundColor(.white))
                            .foregroundColor(day.isSelected ? .blue : .red)
                            .frame(height: 40)
                            .onTapGesture {
                                days[index].isSelected.toggle()  // <--- here
                            }
                    }
                }
            }
        }
        
    }
    

    Or:

    struct ContentView: View {
        
        @State private var days = [
            Day(value: 1, isSelected: true),
            Day(value: 2, isSelected: false),
            Day(value: 3, isSelected: false),
            Day(value: 4, isSelected: true)
        ]
        
        let layout = [
            GridItem(.fixed(40)),
            GridItem(.fixed(40)),
            GridItem(.fixed(40)),
            GridItem(.fixed(40))
        ]
        
        var body: some View {
            ScrollView {
                LazyVGrid(columns: layout) {
                    ForEach(days) { day in
                        Capsule()
                            .overlay(Text("\(day.value)").foregroundColor(.white))
                            .foregroundColor(day.isSelected ? .blue : .red)
                            .frame(height: 40)
                            .onTapGesture {
                                if let index = days.firstIndex(where: {$0.id == day.id}) {
                                    days[index].isSelected.toggle()  // <--- here
                                }
                            }
                    }
                }
            }
        }
    
    }