I am trying to change a value that is stored in class through a custom structure using a picker. I am trying to make an app that lists course name and grade. The picker is in a HStack with a name. I am not able to change the value of a particular element, (in this example, grade) using a Picker. I am using Observable class as I have multiple views which need the access the class.
Here is some of my code,
struct courseItem: Identifiable{
var id = UUID()
let name: String
let grade: String
}
@Observable
class Courses{
var items = [courseItem]()
}
struct ContentView: View{
@State private var courses = Courses()
let grades = ["A", "B", "C"]
var body: some View{
List{
ForEach(courses.items){ item in
HStack{
Text(item.name)
Spacer()
Picker("Grade", selection: item.grade){
ForEach(grades, id: \.self){
Text($0)
}
}
}
}
}
}
}
I know item.grade
should be binding but I get an error when I do that. Right now I get an error saying the compiler is unable to type check the expression in reasonable time. Any help is much appreciated.
Try this approach using bindings $
as shown in the example code,
...to change the value of a particular element, (in this example, grade)
struct courseItem: Identifiable {
let id = UUID() // <--- here
var name: String
var grade: String // <--- here
}
@Observable class Courses {
// <--- for testing
var items: [courseItem] = [courseItem(name: "name-1", grade: "A"),
courseItem(name: "name-2", grade: "B"),
courseItem(name: "name-3", grade: "C")
]
}
struct ContentView: View {
@State private var courses = Courses()
let grades = ["A", "B", "C"]
var body: some View{
List{
ForEach($courses.items){ $item in // <--- here
HStack{
Text(item.name)
Spacer()
Picker("Grade", selection: $item.grade) { // <--- here
ForEach(grades, id: \.self) {
Text($0)
}
}
}
}
}
}
}
If you want to pass courses
to other views, you can use @Bindable var courses: Courses
to allow editing of the desired property, such as:
struct ContentView: View {
@State private var courses = Courses() // <--- here, only one source of truth data
var body: some View {
TestOtherView(courses: courses) // <--- here
}
}
struct TestOtherView: View {
@Bindable var courses: Courses // <--- here
let grades = ["A", "B", "C"]
var body: some View {
List{
ForEach($courses.items){ $item in // <--- here
HStack{
Text(item.name)
Spacer()
Picker("Grade", selection: $item.grade) { // <--- here
ForEach(grades, id: \.self) {
Text($0)
}
}
}
}
}
}
}
Note, if you want to pass courses
to many other views consider using
@Environment(Courses.self) private var courses
see the docs at Managing model data in your app.
For example:
struct ContentView: View{
@State private var courses = Courses()
var body: some View{
TestOtherView()
.environment(courses) // <--- here
}
}
struct TestOtherView: View{
@Environment(Courses.self) private var courses // <--- here
let grades = ["A", "B", "C"]
var body: some View{
@Bindable var courses = courses // <--- here
List{
ForEach($courses.items){ $item in // <--- here
HStack{
Text(item.name)
Spacer()
Picker("Grade", selection: $item.grade) { // <--- here
ForEach(grades, id: \.self) {
Text($0)
}
}
}
}
}
}
}