如何在SwiftUI中正确使用动态成员绑定?

时间:2020-07-08 19:19:54

标签: swiftui swiftui-state

我正在尝试通过Binding变量使用SwiftUI的@Binding成员(通过对@dynamicMemberLookup的支持),但是即使是一个简单的示例,我也可以重新创建多个问题。我最好的猜测是我使用不正确,但是在线文档和示例可能会另外建议。

主要问题(在Catalina,Big Sur和iPadOS 13和14上可重现)是在视图打开时删除项目触发崩溃,并出现索引超出范围错误。

Fatal error: Index out of range: file /AppleInternal/BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1103.8.25.8/swift/stdlib/public/core/ContiguousArrayBuffer.swift, line 444

第二个问题发生在Catalina的文本字段中,尝试编辑文本会隐藏左侧/导航视图。 (在Big Sur上,编辑文本会隐藏右/详细视图,由于导航视图的改进,我认为这是同一问题的不同体现。)

struct Child: Identifiable, Hashable {
    var id = UUID()
    var bar: String = "Text"

    func hash(into hasher: inout Hasher) {
        self.id.hash(into: &hasher)
    }
}

struct ChildView: View {
    let child: Child

    var body: some View {
        Text(child.bar)
    }
}

struct ChildEditor: View {
    @Binding var child: Child

    var body: some View {
        TextField("Text", text: self.$child.bar)
    }
}

struct ContentView: View {
    @State var children: [Child] = []

    func binding(for child: Child) -> Binding<Child> {
        guard let it = children.firstIndex(of: child) else {
            fatalError()
        }
        return $children[it]
    }

    var plusButton: Button<Image> {
        return Button(action: {
            self.children.append(Child())
        }) {
            Image(systemName: "plus")
        }
    }

    func ParentList<Content: View>(_ content: () -> Content) -> some View {
        #if os(macOS)
        return List(content: content)
            .toolbar {
                ToolbarItem {
                    self.plusButton
                }
            }
        // uncomment for 10.15
//        return List {
//            self.plusButton
//            content()
//        }
        #elseif os(iOS)
        return List(content: content)
            .navigationBarItems(trailing: self.plusButton)
        #endif
    }

    var body: some View {
        NavigationView {
            ParentList {
                ForEach(children) { child in
                    NavigationLink(destination: ChildEditor(child: self.binding(for: child))) {
                        ChildView(child: child)
                    }
                }
                .onDelete { offsets in
                    self.children.remove(atOffsets: offsets)
                }
            }
        }
    }
}

我的基本假设是Binding本质上存储了一个指针,因此删除指针将变得无效并触发崩溃,并且编辑文本字段将触发父视图的视图更新,从而使当前内容(这是由Big Sur支持的,有时会抱怨状态变量在视图更新期间被修改,即使它仅用于TextField的初始化中也是如此)。但是,更改为使用类类型和@ObservedObject / @EnvironmentObject(或@StateObject)可以将崩溃(在Catalina和iPadOS 13/14上)延迟到采取或采取任何其他导航措施时没有影响(对大苏尔)。如果删除后使用tag中的NavigationLink选项关闭视图,则也会失败。

第一个问题是:我做错了什么?如果答案是“一切”,那么应该如何在顶级视图中管理一组数据并为嵌套子视图的成员创建绑定?

0 个答案:

没有答案