SwiftUI 上带有 TextField 的可删除表

Posted

技术标签:

【中文标题】SwiftUI 上带有 TextField 的可删除表【英文标题】:Deletable Table with TextField on SwiftUI 【发布时间】:2019-11-18 07:29:50 【问题描述】:

环境

Xcode 11.2.1 (11B500)

问题

为了在 SwiftUI 上使用 TextField 实现可编辑的 teble,我使用了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

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

(lldb) po animals.count
1

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

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

【问题讨论】:

【参考方案1】:

好的,原因在于使用的 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()
    

【讨论】:

该死的,你这次的速度更快 ;) 哦……我们在竞争吗? :) 这个答案太棒了!它也适用于复杂类型! “动态容器”是什么意思,我很难理解为什么这个解决方案有效......【参考方案2】:

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

【讨论】:

不,这无济于事。

以上是关于SwiftUI 上带有 TextField 的可删除表的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 列表中的可编辑文本字段

键盘出现/消失时调整带有 TextField 的 SwiftUI 列表

SwiftUI - 我们如何重新绑定绑定的可选参数?

SwiftUI:如何从核心数据中的 TextField 中保存值

SwiftUI:提交后留在同一个 TextField 上?

SwiftUI for MacOS 中的多行 TextField/TextEditor 表单