SwiftUI上带有TextField的可删除表

时间:2019-11-18 07:29:50

标签: swift swiftui

环境

  • Xcode 11.2.1(11B500)

问题

为了在SwiftUI上使用TextField实现可编辑的高音,我使用ForEach(0..<items.count)处理索引。

import SwiftUI

struct DummyView: View {
    @State var animals: [String] = ["?", "?"]

    var body: some View {
        List {
            EditButton()
            ForEach(0..<animals.count) { i in
                TextField("", text: self.$animals[i])
            }
        }
    }
}

但是,如果将该表更改为可删除,则会出现问题。

import SwiftUI

struct DummyView: View {
    @State var animals: [String] = ["?", "?"]

    var body: some View {
        List {
            EditButton()
            ForEach(0..<animals.count) { i in
                TextField("", text: self.$animals[i]) // Thread 1: Fatal error: Index out of range
            }
            .onDelete { indexSet in
                self.animals.remove(atOffsets: indexSet) // Delete "?" from animals
            }
        }
    }
}

Thread 1: Fatal error: Index out of range when delete item

?已从动物中删除,即使animal.count为1,ForEach循环也似乎运行了两次。

(lldb) po animals.count
1

(lldb) po animals
▿ 1 element
  - 0 : "?"

请给我有关Foreach和TextField组合的建议。
谢谢。

2 个答案:

答案 0 :(得分:2)

好吧,原因在于使用过的ForEach构造函数的文档中(如您所见,范围是恒定的,因此ForEach会获取初始范围并保存它):

/// Creates an instance that computes views on demand over a *constant*
/// range.
///
/// This instance only reads the initial value of `data` and so it does not
/// need to identify views across updates.
///
/// To compute views on demand over a dynamic range use
/// `ForEach(_:id:content:)`.
public init(_ data: Range<Int>, @ViewBuilder content: @escaping (Int) -> Content)

因此解决方案将是提供动态容器。在下面,您可以找到可行方法的演示。

完整的模块代码

import SwiftUI

struct DummyView: View {
    @State var animals: [String] = ["?", "?"]

    var body: some View {
        VStack {
            HStack {
                EditButton()
                Button(action: { self.animals.append("Animal \(self.animals.count + 1)") }, label: {Text("Add")})
            }
            List {
                ForEach(animals, id: \.self) { item in
                    EditorView(container: self.$animals, index: self.animals.firstIndex(of: item)!, text: item)
                }
                .onDelete { indexSet in
                    self.animals.remove(atOffsets: indexSet) // Delete "?" from animals
                }
            }
        }
    }
}

struct EditorView : View {
    var container: Binding<[String]>
    var index: Int

    @State var text: String

    var body: some View {
        TextField("", text: self.$text, onCommit: {
            self.container.wrappedValue[self.index] = self.text
        })
    }
}

struct TestForEachCapture_Previews: PreviewProvider {
    static var previews: some View {
        DummyView()
    }
}

答案 1 :(得分:0)

这是因为editbutton在您的列表中。将其放置在导航栏中或更好。