Search code examples
swiftselectswiftuicolorsgrid

How can I change a specific number of items in a LazyGrid SwiftUI?


I want to be able to select a number of items (e.g. the first 20) in the grid and change the color of the elements.

import SwiftUI

struct ContentView: View {
    @State var showView = false
    @State private var birthDate = Date.now
    struct Day: Identifiable {
        let id = UUID()
        let value: Int
    }
    
    struct Month {
        let name: String
        let numberOfDays: Int
        var days: [Day]

        init(name: String, numberOfDays: Int) {
            self.name = name
            self.numberOfDays = numberOfDays
            self.days = []

            for n in 1...numberOfDays {
                self.days.append(Day(value: n))
            }

        }
    }
    
    let year = [
        Month(name: "Youth", numberOfDays: 12),
        Month(name: "Teenager", numberOfDays: 7),
        Month(name: "20s - 30s", numberOfDays: 20),
        Month(name: "Middle ages", numberOfDays: 26),
        Month(name: "Retirement", numberOfDays: 35),
    ]
    let layout = [
            GridItem(.flexible(minimum: 40)),
            GridItem(.flexible(minimum: 40)),
            GridItem(.flexible(minimum: 40)),
            GridItem(.flexible(minimum: 40)),
            GridItem(.flexible(minimum: 40)),
        ]
    
    var body: some View {
        VStack {
                   DatePicker(selection: $birthDate, in: ...Date.now, displayedComponents: .date) {
                   }

                   Text("Your Birthdate is \(birthDate.formatted(date: .long, time: .omitted))")
               
            ScrollView {
                
                LazyVGrid(columns: layout, pinnedViews: [.sectionHeaders]) {
                    ForEach(year, id: \.name){ month in
                        Section(header: Text(verbatim: month.name).font(.headline)) {
                            ForEach(month.days) { day in
                                Capsule()
                            }
                       }
                    }
                }.padding(5)
            }  
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

This is what it looks like.

screenshot of the app currently

and this is what I want to happen - ill integrate the date but need a way to change a specific number of these (in this example 7 of them have been changed:

same screenshot with 7 elements selected and changed colour from blue to black

I have tried a ForEach but can't seem to get it working, any help would be appreciated.


Solution

  • Hope this help ):

    import SwiftUI
    
    struct ContentView: View {
        @State var showView = false
        @State private var birthDate = Date.now
        struct Day: Identifiable {
            let id = UUID()
            let value: Int
        }
    
        struct Month {
            let name: String
            let numberOfDays: Int
            var days: [Day]
    
            init(name: String, numberOfDays: Int) {
                self.name = name
                self.numberOfDays = numberOfDays
                days = []
    
                for n in 1 ... numberOfDays {
                    days.append(Day(value: n))
                }
            }
        }
    
        let year = [
            Month(name: "Youth", numberOfDays: 12),
            Month(name: "Teenager", numberOfDays: 7),
            Month(name: "20s - 30s", numberOfDays: 20),
            Month(name: "Middle ages", numberOfDays: 26),
            Month(name: "Retirement", numberOfDays: 35),
        ]
        let layout = [
            GridItem(.flexible(minimum: 40)),
            GridItem(.flexible(minimum: 40)),
            GridItem(.flexible(minimum: 40)),
            GridItem(.flexible(minimum: 40)),
            GridItem(.flexible(minimum: 40)),
        ]
        @State private var selected: Set<Day.ID> = []
        init() {
            guard let selectedDays = year.first?.days else { return }
            _selected = .init(initialValue: .init(selectedDays.map { $0.id }))
        }
    
        var body: some View {
            VStack {
                DatePicker(selection: $birthDate, in: ...Date.now, displayedComponents: .date) {}
    
                Text("Your Birthdate is \(birthDate.formatted(date: .long, time: .omitted))")
    
                ScrollView {
                    LazyVGrid(columns: layout, pinnedViews: [.sectionHeaders]) {
                        ForEach(year, id: \.name) { month in
                            Section(header: Text(verbatim: month.name).font(.headline)) {
                                ForEach(month.days) { day in
                                    Capsule()
                                        .fill(isSelected(id: day.id) ? .gray : .cyan)
                                }
                            }
                        }
                    }.padding(5)
                }
            }
        }
    
        private func isSelected(id: Day.ID) -> Bool {
            return selected.contains(id)
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }