【问题标题】:SwiftUI: Deleting last row in ForEachSwiftUI:删除 ForEach 中的最后一行
【发布时间】:2020-04-25 23:54:31
【问题描述】:

我正在尝试删除 ForEach 中的行。删除最后一行总是会引发索引超出范围异常。删除任何其他行不会。

ForEach(Array(player.scores.enumerated()), id: \.element) { index, score in
    HStack {
        if self.isEditSelected {
            Button(action: {
                self.player.scores.remove(at: index)
            }, label: {
                Image("delete")
            })
        }        
        TextField("\(score)", value: self.$player.scores[index], formatter: NumberFormatter())
    }
}

我尝试过使用ForEach(player.indices...)ForEach(player.scores...),但遇到了同样的问题。

在我看来,崩溃发生在这里self.$player.scores[index],因为将索引硬编码为除最后一行工作之外的任何值。

有谁知道如何解决这个问题?或者如果有更好的方法。

【问题讨论】:

标签: ios swiftui


【解决方案1】:

这里是修复

ForEach(Array(player.scores.enumerated()), id: \.element) { index, score in
    HStack {
        if self.isEditSelected {
            Button(action: {
                self.player.scores.remove(at: index)
            }, label: {
                Image("delete")
            })
        }        
        TextField("\(score)", value: Binding(   // << use proxy binding !!
            get: { self.player.scores[index] },
            set: { self.player.scores[index] = $0 }), 
            formatter: NumberFormatter())
    }
}

【讨论】:

  • 太棒了。这是在具有非恒定范围的情况下枚举 Array 的最佳方式!
  • 你知道@Asperi 为什么代理绑定会阻止视图在索引更新之前尝试重绘吗?它是一种 hack 还是一种合法的编码方式?
  • @Asperi 为什么它解决了这个问题?出了什么问题……为什么现在是正确的?
【解决方案2】:

基于@Asperi 的回答

public extension Binding where Value: Equatable {
    static func proxy(_ source: Binding<Value>) -> Binding<Value> {
            self.init(
                get: { source.wrappedValue },
                set: { source.wrappedValue = $0 }
            )
    }
}

你可以这样使用:

TextField("Name", text: .proxy($variable))

【讨论】:

    【解决方案3】:

    Xcode 13.0 beta 引入了一种在集合元素和 ForEach / List 构建的视图之间建立双向绑定的新方法。 此方法修复了与删除最后一行相关的崩溃问题。

    struct Score: Identifiable {
        let id = UUID()
        var value: Int
    }
    
    struct Player {
        var scores: [Score] = (1...10).map {_ in .init(value: Int.random(in: 0...25))}
    }
    
    struct BindingTest: View {
        @State private var player = Player()
    
        var body: some View {
            List {
                ForEach($player.scores) { $score in
                    HStack {
                        TextField("\(score.value)", value: $score.value,
                            formatter: NumberFormatter())
                    }
                }
                .onDelete { player.scores.remove(atOffsets: $0)}
            }
        }
    }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-01-14
      • 1970-01-01
      • 1970-01-01
      • 2016-08-13
      • 2018-11-03
      • 2013-11-24
      • 2014-02-05
      相关资源
      最近更新 更多