我确定这很愚蠢,但是当另一种状态改变时,应该如何重置子视图的状态值?
例如,下面的代码显示2个文件夹,分别包含2和3个项目,可以进行编辑。
如果您选择第二个文件夹(工作)和其第三个项目(彼得),然后选择第一个文件夹(家庭),则由于selectedItemIndex
超出范围,应用程序将崩溃。
我试图在初始化视图时“重置”状态值,但似乎像这样更改状态会触发“运行时:SwiftUI:在视图更新期间修改状态,这将导致未定义的行为”。警告。
init(items: Binding<[Item]>) {
self._items = items
self._selectedItemIndex = State(wrappedValue: 0)
}
执行此操作的正确方法是什么?谢谢!
这是代码:
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) } }
答案 0 :(得分:0)
感谢parent-module
提出修复建议:
ItemsView(items: $store.data[selectedFolderIndex!].items).id(selectedRowIndex)
答案 1 :(得分:0)
ContentView.swift
的完全可播放的示例草稿。在两种编辑模式(非活动/活动行选择)中进行播放,并根据需要进行选择。
import SwiftUI
struct ItemStore {
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 {
var id = UUID()
var name: String
var content = Date().description
}
struct ContentView: View {
@State var store: ItemStore
@State var selectedFolderIndex: Int? = 0
@State private var editMode = EditMode.inactive
var body: some View {
NavigationView {
VStack {
// FOLDERS
List(selection: $selectedFolderIndex) {
Section(header: Text("Groups")) {
ForEach(store.data.indexed(), id: \.1.id) { index, folder in
HStack {
Text(folder.name).tag(index)
Spacer()
}
.background(Color.white) //make the whole row tapable, not just the text
.frame(maxWidth: .infinity)
.multilineTextAlignment(.leading)
.onTapGesture {
self.selectedFolderIndex = index
}
}.onDelete(perform: delete)
}
}
.listStyle(GroupedListStyle())
.id(selectedFolderIndex)
// ITEMS
if selectedFolderIndex != nil && (($store.data.wrappedValue.startIndex..<$store.data.wrappedValue.endIndex).contains(selectedFolderIndex!) ){
ItemsView(items: $store.data[selectedFolderIndex!].items)
}
}
.navigationBarTitle("Title")
.navigationBarItems(trailing: EditButton())
.environment(\.editMode, $editMode)
}
}
func delete(at offsets: IndexSet) {
$store.wrappedValue.data.remove(atOffsets: offsets) // Note projected value! `store.data.remove() will not modify SwiftUI on changes and it will crash because of invalid index.
}
}
struct ItemsView: View {
@Binding var items: [Item]
@State var selectedDetailIndex: Int?
var body: some View {
HStack {
List(selection: $selectedDetailIndex) {
ForEach(items.indexed(), id: \.1.id) { index, item in
Text(item.name).tag(index)
.onTapGesture {
self.selectedDetailIndex = index
}
}
}
if selectedDetailIndex != nil && (($items.wrappedValue.startIndex..<$items.wrappedValue.endIndex).contains(selectedDetailIndex!) ) {
DetailView(item: $items[selectedDetailIndex!])
.padding()
}
}
}
}
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<Base: RandomAccessCollection>: 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<Self> {
IndexedCollection(base: self)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(store: ItemStore())
}
}