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.
CodePudding user response:
you need a new property in your
Day
struct to keep track of selection. I called itselected
.you have to declare
year
as a @State var – so it is the source of truth for your view, and can be modified.To make
day
changeable in the view, you have to pass year, month and day down using the $ initializer ofForEach
.
( 2. and 3. can be solved more elegantly by using an ObservableObject class for your base data)
Change the capsule color based on
day.selected
.On Tap gesture toggle
day.selected
.
struct ContentView: View {
@State var showView = false
@State private var birthDate = Date.now
struct Day: Identifiable {
let id = UUID()
let value: Int
var selected: Bool = false // new property here
}
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))
}
}
}
@State var year = [ // define year as State to make it changeable
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 // binding init
Section(header: Text(verbatim: month.name).font(.headline)) {
ForEach($month.days) { $day in // binding init
Capsule()
.frame(height: 20)
.foregroundColor(day.selected ? .red : .cyan) // different colors based on selected
.onTapGesture {
day.selected.toggle() // on Tap toggle selected
}
}
}
}
}.padding(5)
}
}
}
}
CodePudding user response:
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()
}
}