gpt4 book ai didi

swift - 使用结构而不是类的简单 SwiftUI CRUD?

转载 作者:行者123 更新时间:2023-12-04 17:26:12 29 4
gpt4 key购买 nike

我有一个复杂的数据结构,它使用值类型(结构和枚举),并且我面临着使基本 CRUD 工作的主要问题。具体来说:

  • 如何最好地“重新绑定(bind)” ForEach 中的值以供 subview 编辑
  • 如何移除/删除值

  • 重新装订
    如果我有一个项目数组 @State@Binding ,为什么没有一种简单的方法将每个元素绑定(bind)到 View ?例如:
    import SwiftUI

    struct Item: Identifiable {
    var id = UUID()
    var name: String
    }

    struct ContentView: View {
    @State var items: [Item]
    var body: some View {
    VStack {
    ForEach(items, id: \.id) { item in
    TextField("name", text: $item) // 🛑 Cannot find '$item' in scope
    }
    }
    }
    }
    解决方法
    我已经能够通过引入一个辅助函数来为循环中的项目找到正确的索引来解决这个问题:
    struct ContentView: View {
    @State var items: [Item]

    func index(of item: Item) -> Int {
    items.firstIndex { $0.id == item.id } ?? -1
    }

    var body: some View {
    VStack {
    ForEach(items, id: \.id) { item in
    TextField("name", text: $items[index(of: item)].name)
    }
    }
    }
    }
    然而,这感觉笨拙而且可能很危险。
    删除
    一个更大的问题:你应该如何正确删除一个元素?这听起来像是一个基本问题,但请考虑以下问题:
    struct ContentView: View {
    @State var items: [Item]

    func index(of item: Item) -> Int {
    items.firstIndex { $0.id == item.id } ?? -1
    }

    var body: some View {
    VStack {
    ForEach(items, id: \.id) { item in
    TextField("name", text: $items[index(of: item)].name)
    Button( action: {
    items.remove(at: index(of: item))
    }) {
    Text("Delete")
    }
    }
    }
    }
    }
    单击前几个项目上的“删除”按钮按预期工作,但尝试删除最后一个项目会导致 Fatal error: Index out of range ...
    我的特定用例没有映射到列表,所以我不能在那里使用删除助手。
    引用类型
    我知道引用类型使这件事变得更容易,特别是如果它们可以符合 @ObservableObject .但是,我有一个庞大的、嵌套的、预先存在的值类型,它不容易转换为类。
    非常感激任何的帮助!
    更新:建议的解决方案
  • Deleting List Elements from SwiftUI's list :接受的答案提出了一个复杂的自定义绑定(bind)包装器。 Swift 功能强大,因此可以通过精心设计的变通方法解决许多问题,但我认为拥有可编辑项目列表不需要精心设计的变通方法。
  • 使用 State 或私有(private)变量将 View 标记为“已删除”,然后有条件地隐藏它们,以避免越界错误。这可以工作,但感觉就像一个黑客,应该由框架处理的事情。
  • 最佳答案

    我确认更适合 CRUD 的方法是使用 ObservableObject基于类的 View 模型。 @NewDev 在评论中提供的答案是该方法的一个很好的演示。
    但是,如果您已经有一个庞大的、嵌套的、预先存在的值类型并且不容易转换为类,则可以通过 @State/@Binding 来解决。 ,但您应该考虑什么/何时/以及如何更新每个 View 和每个顺序 - 这是所有此类索引超出删除问题(以及更多)的起源。
    这是如何打破此更新依赖项以避免崩溃并仍然使用值类型的方法的演示。
    根据您使用 Xcode 11.4/iOS 13.4 (SwiftUI 1.0+) 的代码进行测试

    struct ContentView: View {
    @State var items: [Item] = [Item(name: "Name1"), Item(name: "Name2"), Item(name: "Name3")]

    func index(of item: Item) -> Int {
    items.firstIndex { $0.id == item.id } ?? -1
    }

    var body: some View {
    VStack {
    ForEach(items, id: \.id) { item in
    // separate dependent views as much as possible to make them as
    // smaller/lighter as possible
    ItemRowView(items: self.$items, index: self.index(of: item))
    }
    }
    }
    }

    struct ItemRowView: View {
    @Binding var items: [Item]
    let index: Int

    @State private var destroyed = false // internal state to validate self

    var body: some View {
    // proxy binding to have possibility for validation
    let binding = Binding(
    get: { self.destroyed ? "" : self.items[self.index].name },
    set: { self.items[self.index].name = $0 }
    )

    return HStack {
    if !destroyed { // safety check against extra update
    TextField("name", text: binding)
    Button( action: {
    self.destroyed = true
    self.$items.wrappedValue.remove(at: self.index)
    }) {
    Text("Delete")
    }
    }
    }
    }
    }
    是的,这不是一个简单的解决方案,但有时我们需要它。

    关于swift - 使用结构而不是类的简单 SwiftUI CRUD?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63232386/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com