I have some buttons and I want to apply different background color on selection for each button but I want to maintain the state within the array for selection and deselection. I have created a class for a single object by confirming the ObservableObject. Then I am creating the array of the the same objects and trying to store it with in a StateObject variable but it's showing an error like "Generic struct 'StateObject' requires that '[TimeSlot]' conform to 'ObservableObject'". How can I achieve it.
import SwiftUI
class TimeSlot: ObservableObject {
@State var timelot: String
@State var state: Bool
init(timelot: String, state: Bool) {
self.timelot = timelot
self.state = state
}
}
struct DateTimeSelectionView: View {
@StateObject var timeSlots = [
TimeSlot(timelot: "06:30", state: false),
TimeSlot(timelot: "07:30", state: false),
TimeSlot(timelot: "08:00", state: false),
TimeSlot(timelot: "08:30", state: false)
]
var body: some View {
ScrollView {
VStack(alignment: .leading) {
Text("Select Time:")
.multilineTextAlignment(.leading)
.font(.system(size: 20, weight: .medium, design: .default))
.foregroundColor(AppTheme.appThemeOrange)
VStack(alignment: .leading, spacing: 15) {
HStack {
ForEach(timeSlots) { index in
Button {
timeSlots[index].state.toggle()
} label: {
Text("\(timeSlots[index].timelot) PM")
.foregroundColor(.white)
.font(.system(size: 16, weight: .medium, design: .default))
}.frame(width: 74)
.padding(.horizontal, 6)
.padding(.vertical, 10)
.background(AppTheme.appThemeBlue)
.cornerRadius(2)
}
}
}
}.padding(.horizontal, 10)
}
}
}
struct DateTimeSelectionView_Previews: PreviewProvider {
static var previews: some View {
DateTimeSelectionView()
}
}
CodePudding user response:
There are a lot of issues in the data handling here. Try this commented code:
// this is a model container so no need for observable object or class implementation
struct TimeSlot: Hashable {
// @State is only valid in Views
var timelot: String
var state: Bool
// initializer is unnecessary
}
struct DateTimeSelectionView: View {
// a collection of models should be @State
@State private var timeSlots = [
TimeSlot(timelot: "06:30", state: false),
TimeSlot(timelot: "07:30", state: false),
TimeSlot(timelot: "08:00", state: false),
TimeSlot(timelot: "08:30", state: false)
]
var body: some View {
ScrollView {
VStack(alignment: .leading) {
Text("Select Time:")
.multilineTextAlignment(.leading)
.font(.system(size: 20, weight: .medium, design: .default))
VStack(alignment: .leading, spacing: 15) {
HStack {
// if you want to change the item inside the ForEach loop
// you need a binding to it
// additionally you need to set the id
ForEach($timeSlots, id: \.self) { $slot in
// inside the loop you get a binding to the item itself
// use it here to display and manipulate it
Button {
slot.state.toggle()
} label: {
Text("\(slot.timelot) PM")
// added this to help visualize the state
.foregroundColor(slot.state ? .blue : .red)
.font(.system(size: 16, weight: .medium, design: .default))
}.frame(width: 74)
.padding(.horizontal, 6)
.padding(.vertical, 10)
.cornerRadius(2)
}
}
}
}.padding(.horizontal, 10)
}
}
}