I'm sure it's something very silly but how should one reset the state value of a child view when another state has changed?
For example, the code below shows 2 folders, which respectively have 2 and 3 items., which can be edited.
If you select the second folder (Work) and its 3rd item (Peter) and then select the first folder (Home), the app crashes since selectedItemIndex
is out of bounds.
I tried to "reset" the state value when the view gets initialized but it seems like changing the state like such triggers out a "runtime: SwiftUI: Modifying state during view update, this will cause undefined behavior." warning.
init(items: Binding<[Item]>) {
self._items = items
self._selectedItemIndex = State(wrappedValue: 0)
}
What is the proper way to do this? Thanks!
Here's the code:
AppDelegate.swift
import Cocoa import SwiftUI @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { var window: NSWindow! func applicationDidFinishLaunching(_ aNotification: Notification) { // Create the SwiftUI view that provides the window contents. let store = ItemStore() let contentView = ContentView(store: store) // Create the window and set the content view. window = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 480, height: 300), styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView], backing: .buffered, defer: false) window.center() window.setFrameAutosaveName("Main Window") window.contentView = NSHostingView(rootView: contentView) window.makeKeyAndOrderFront(nil) } func applicationWillTerminate(_ aNotification: Notification) { // Insert code here to tear down your application } }
ContentView.swift
import SwiftUI final class ItemStore: ObservableObject { @Published var data: [Folder] = [Folder(name: "Home", items: [Item(name: "Mark"), Item(name: "Vincent")]), Folder(name: "Work", items:[Item(name: "Joseph"), Item(name: "Phil"), Item(name: "Peter")])] } struct Folder: Identifiable { var id = UUID() var name: String var items: [Item] } struct Item: Identifiable { static func == (lhs: Item, rhs: Item) -> Bool { return true } var id = UUID() var name: String var content = Date().description init(name: String) { self.name = name } } struct ContentView: View { @ObservedObject var store: ItemStore @State var selectedFolderIndex: Int? var body: some View { HSplitView { // FOLDERS List(selection: $selectedFolderIndex) { Section(header: Text("Groups")) { ForEach(store.data.indexed(), id: \.1.id) { index, folder in Text(folder.name).tag(index) } }.collapsible(false) } .listStyle(SidebarListStyle()) // ITEMS if selectedFolderIndex != nil { ItemsView(items: $store.data[selectedFolderIndex!].items) } } .frame(minWidth: 800, maxWidth: .infinity, maxHeight: .infinity) } } struct ItemsView: View { @Binding var items: [Item] @State var selectedItemIndex: Int? var body: some View { HSplitView { List(selection: $selectedItemIndex) { ForEach(items.indexed(), id: \.1.id) { index, item in Text(item.name).tag(index) } } .frame(width: 300) if selectedItemIndex != nil { DetailView(item: $items[selectedItemIndex!]) .padding() .frame(minWidth: 200, maxHeight: .infinity) } } } init(items: Binding) { self._items = items self._selectedItemIndex = State(wrappedValue: 0) } } struct DetailView: View { @Binding var item: Item var body: some View { VStack { TextField("", text: $item.name) } } } // Credit: https://swiftwithmajid.com/2019/07/03/managing-data-flow-in-swiftui/ struct IndexedCollection: RandomAccessCollection { typealias Index = Base.Index typealias Element = (index: Index, element: Base.Element) let base: Base var startIndex: Index { base.startIndex } var endIndex: Index { base.endIndex } func index(after i: Index) -> Index { base.index(after: i) } func index(before i: Index) -> Index { base.index(before: i) } func index(_ i: Index, offsetBy distance: Int) -> Index { base.index(i, offsetBy: distance) } subscript(position: Index) -> Element { (index: position, element: base[position]) } } extension RandomAccessCollection { func indexed() -> IndexedCollection { IndexedCollection(base: self) } }
Binding<[Item]>
becameBinding
for example. – Rob N