ARTICLE AD BOX
I have an outer and an inner ForEach View. The outer view displays a "category" and the inner view displays a list of foods in that "category" (think: shopping list). When I click on the box, I don't see the check mark. I have built a similar bit of code with just as single ForEach view and the checkbox works as expected. The idea is I would take this to grocery store and when I put something in my trolly I would check the box.
Building for iPhone 17. Behavior in simulator and canvas is the same. I use Toggle("", isOn: Binding({get; set;}) both of which call functions in the data model. I've instrumented the functions to print info to the console so I know they are getting called.
I was planning to use the "toggle" function to send a message to my server (which is why I'm using (get; set;)
The code I'm supplying is a single file (hopefully to keep things simple) and the data model is hard coded with test values.
// // ContentView.swift // StupidMe // // Created by CARL ADLER on 2/4/26. // import SwiftUI import Foundation import Combine class ListItem{ let id = UUID() var description:String var isChecked:Bool init(description: String, isChecked : Bool) { self.description = description self.isChecked = isChecked } } class CategorizedItem{ let id = UUID() var category : String var items : [ListItem] init(category: String, items: [ListItem]) { self.category = category self.items = items } } @Observable class ViewModel{ var activeList : [CategorizedItem] = [] init() { // Hard Coding Test Values (these normally are fetched via my server's API) let Dairy1 = ListItem(description: "Milk", isChecked: false) let Dairy2 = ListItem(description: "Yogurt", isChecked: false) let Dairy3 = ListItem(description: "Cheese", isChecked: false) let DairyList = [Dairy1, Dairy2, Dairy3] let cat1 = CategorizedItem(category: "Dairy", items: DairyList) activeList.append(cat1) let groc1 = ListItem(description: "Onion", isChecked: false) let groc2 = ListItem(description: "Carrot", isChecked: false) let groc3 = ListItem(description: "Tomato", isChecked: false) let grocList : [ListItem] = [groc1, groc2, groc3] let cat2 = CategorizedItem(category: "Groceries", items: grocList) activeList.append(cat2) let bak1 = ListItem(description: "Bread", isChecked: false) let bak2 = ListItem(description: "Bagels", isChecked: false) let bak3 = ListItem(description: "English Muffins", isChecked: false) let bakList : [ListItem] = [bak1, bak2, bak3] let cat3 = CategorizedItem(category: "Bakery", items: bakList) activeList.append(cat3) } func fetchIsChecked(_ outerIx:Int, _ innerIx:Int) -> Bool{ print("FETCHING outerIx = \(outerIx), innerIx=\(innerIx) checked= \(activeList[outerIx].items[innerIx].isChecked) item=\(activeList[outerIx].items[innerIx].description)") return activeList[outerIx].items[innerIx].isChecked } func toggleChecked(_ outerIx:Int, _ innerIx:Int){ print("TOGGLING outterIx= \(outerIx), innterIx=\(innerIx) checked= \(activeList[outerIx].items[innerIx].isChecked) item=\(activeList[outerIx].items[innerIx].description)") activeList[outerIx].items[innerIx].isChecked.toggle() } } struct CheckboxToggleStyle: ToggleStyle { func makeBody(configuration: Configuration) -> some View { HStack { RoundedRectangle(cornerRadius: 5.0) .stroke(lineWidth: 1) .frame(width: 40, height: 40, alignment: .center) .overlay{ Image(systemName: configuration.isOn ? "checkmark" : "square.dotted").font(.system(size: 40)) } .onTapGesture{configuration.isOn.toggle()} } } } struct ContentView: View { var vm: ViewModel = ViewModel() var body: some View { List{ VStack (alignment: .leading) { // "OUTER" ForEach ForEach(vm.activeList.indices, id: \.self) { outerIx in VStack(alignment: .leading) { Text("\(vm.activeList[outerIx].category)") .font(.title) VStack(alignment: .leading){ // "INNER" ForEach ForEach(vm.activeList[outerIx].items.indices, id: \.self) {innerIx in HStack{ Toggle("", isOn: Binding( get: { vm.fetchIsChecked(outerIx, innerIx) }, set: { newValue in vm.toggleChecked(outerIx, innerIx) } )) .toggleStyle(CheckboxToggleStyle()) Text(vm.activeList[outerIx].items[innerIx].description) } } } } } } } } } #Preview { ContentView() } @main struct StupidMeApp: App { var body: some Scene { WindowGroup { ContentView() } } }