SwiftUI:删除 ForEach 中的最后一行

Posted

技术标签:

【中文标题】SwiftUI:删除 ForEach 中的最后一行【英文标题】:SwiftUI: Deleting last row in 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],因为将索引硬编码为除最后一行工作之外的任何值。

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

【问题讨论】:

这能回答你的问题吗? Deletable Table with TextField on 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)
        
    


【讨论】:

以上是关于SwiftUI:删除 ForEach 中的最后一行的主要内容,如果未能解决你的问题,请参考以下文章

删除最后一个数组元素时,SwiftUI列表ForEach索引超出范围

如何着色/删除 SwiftUI 列表中最后一行和倒数第二行之间的分隔符?

SwiftUI:删除列表中的动画?

从 SwiftUI 中的列表中删除绑定

SwiftUI 中 ForEach 结构的最后一个参数的函数类型是啥?

SwiftUI NavigationView 与 List 内 ForEach 中的 NavigationLink 来回跳转