【问题标题】:How to reset child view state variable with SwiftUI?如何使用 SwiftUI 重置子视图状态变量?
【发布时间】:2020-07-21 10:36:04
【问题描述】:

我确定这很愚蠢,但是当另一个状态发生变化时,应该如何重置子视图的状态值?

例如下面的代码显示了2个文件夹,分别有2个和3个项目,可以编辑。

如果您选择第二个文件夹 (Work) 及其第三个项目 (Peter),然后选择第一个文件夹 (Home),则应用程序会崩溃,因为 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<[Item]>) {
        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<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)
    }
}

【问题讨论】:

  • 这能回答你的问题吗? Handling derived state in SwiftUI
  • 我尝试将您的代码粘贴到 Xcode 11.4 中,但出现了很多编译器错误,因此很难进行试验。 IndexedCollection 上应该有类型参数吗?有些东西可能没有成为您粘贴到问题中的内容。例如,我看到Binding&lt;[Item]&gt; 变成了Binding

标签: ios swift swiftui


【解决方案1】:

感谢 @jordanpittman 提出修复建议:

ItemsView(items: $store.data[selectedFolderIndex!].items).id(selectedRowIndex)

来源:https://swiftui-lab.com/swiftui-id

【讨论】:

    【解决方案2】:

    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())
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-29
      • 2021-02-05
      • 2020-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-26
      相关资源
      最近更新 更多