Как правильно использовать динамические привязки членов в SwiftUI?

Я пытаюсь использовать SwiftUI Binding члены из @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) до того момента, когда любое другое действие навигации будет выполнено или не окажет никакого эффекта (в Big Sur). С использованиемtag вариант в NavigationLink отклонить представление, если оно было удалено, также не удалось.

Первый вопрос: что я делаю не так? Если ответ на этот вопрос - "Все", как следует управлять массивом данных в представлении верхнего уровня и создавать привязки к членам для вложенных подпредставлений?

0 ответов

Другие вопросы по тегам