如何仅使用 ForEach 而不是 List 从动态范围中删除

Posted

技术标签:

【中文标题】如何仅使用 ForEach 而不是 List 从动态范围中删除【英文标题】:How to delete from dynamic range using only ForEach and not List 【发布时间】:2020-06-15 10:59:24 【问题描述】:

我找不到从 ForEach 循环中使用的动态数组中删除的方法。我一直在寻找没有运气。许多答案使用 List 或在其 ForEach 中没有绑定。而且我不想使用列表,因为它很难完全定制它的设计。

以下是从数组中添加和删除元素的示例代码。该数组用于显示玩家的动态列表。

在解开 ForEach 循环中的可选项后,移除一个播放器会产生一个超出范围的索引。

import SwiftUI

struct GameRecapView: View 

    @State private var game = Game(players: [Player(name: "Steph"),Player(name: "Kim")])
    @State private var shape = Shape.circle

    var body: some View 
        VStack 
            ForEach(self.game.players ,id: \.id)  player in
                PlayerView(
                    player: self.$game.players[self.game.players.firstIndex(where: $0.id == player.id)!],
                    shape: self.$shape)
            
            Spacer()
            Button(action: 
                self.toggleShape()
            ) 
                Text("Change shape")
            
            HStack 
                Button(action: 
                    self.addPlayer(player: Player(name: "Eddye"))
                ) 
                    Text("+")
                
                Button(action: 
                    self.removeLastPlayer()
                ) 
                    Text("-")
                
            
        
    

    func toggleShape()
        if self.shape == .circle 
            self.shape = .square
         else 
            self.shape = .circle
        
    

    func addPlayer(player : Player) 
        self.game.players.append(player)
    

    func removeLastPlayer()
        self.game.players.removeLast()
    

    func removeItems(at offsets: IndexSet) 
        self.game.players.remove(atOffsets: offsets)
    



struct PlayerView: View 

    @Binding var player : Player
    @Binding var shape : Shape

    var letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t"]

    var body: some View 

        VStack 
            ZStack
                Text(String(self.player.name.first!.uppercased()))
                if shape == .square 
                    Rectangle().stroke().frame(width: 50, height: 50)
                 else 
                    Circle().stroke().frame(width: 50, height: 50)
                

            

            Button(action: 
                self.player.name = self.letters.randomElement()!
            ) 
                Text("Change Name")
            

        
    


struct Game 
    var players : [Player]


struct Player : Identifiable 
    var id = UUID()
    var name : String


enum Shape 
    case
    circle ,
    square


struct GameRecapView_Previews: PreviewProvider 
    static var previews: some View 
        GameRecapView()
    

【问题讨论】:

【参考方案1】:

看看这个:(测试和工作)

始终致力于单一的事实来源(正如苹果所说的那样),而不是副本。确保您的更改将由 ObservableObject 完成。

class Data : ObservableObject 

    @Published var game = Game(players: [Player(name: "Steph"),Player(name: "Kim")])


struct GameRecapView: View 

    @EnvironmentObject var data : Data

    @State private var shape = Shape.circle

    var body: some View 
        VStack 
            ForEach(self.data.game.players ,id: \.id)  player in
                PlayerView(
                    player: self.data.game.players[self.data.game.players.firstIndex(where: $0.id == player.id)!],
                    shape: self.$shape)
            
            Spacer()
            Button(action: 
                self.toggleShape()
            ) 
                Text("Change shape")
            
            HStack 
                Button(action: 
                    self.addPlayer(player: Player(name: "Eddye"))
                ) 
                    Text("+")
                
                Button(action: 
                    self.removeLastPlayer()
                ) 
                    Text("-")
                
            
        
    

    func toggleShape()
        if self.shape == .circle 
            self.shape = .square
         else 
            self.shape = .circle
        
    

    func addPlayer(player : Player) 
        self.data.game.players.append(player)
    

    func removeLastPlayer()
        self.data.game.players.removeLast()
    

    func removeItems(at offsets: IndexSet) 
        self.data.game.players.remove(atOffsets: offsets)
    



struct PlayerView: View 

    @EnvironmentObject var data : Data

    var player : Player
    @Binding var shape : Shape

    var letters = ["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t"]

    var body: some View 

        VStack 
            ZStack
                Text(String(self.player.name.first!.uppercased()))
                if shape == .square 
                    Rectangle().stroke().frame(width: 50, height: 50)
                 else 
                    Circle().stroke().frame(width: 50, height: 50)
                

            

            Button(action: 
                let playerIndex = self.data.game.players.firstIndex(where: $0.id == self.player.id)
                self.data.game.players[playerIndex!].name = self.letters.randomElement()!
            ) 
                Text("Change Name")
            

        
    


struct Game 
    var players : [Player]


struct Player : Identifiable 
    var id = UUID()
    var name : String


enum Shape 
    case
    circle ,
    square


struct ContentView: View 

    var body : some View 
        Text("wtf")
    


struct GameRecapView_Previews: PreviewProvider 
    static var previews: some View 
        GameRecapView().environmentObject(Data())
    


struct ContentView_Previews: PreviewProvider 
    static var previews: some View 
        ContentView()
    

【讨论】:

好吧,我会尝试的(我远离计算机,否则我会发疯;)但是为什么我的架构不是基于单一事实来源?哦,是因为 ForEach 枚举了一个游戏副本吗? 是的,它会复制 该死的 SwiftUI 3 个月,今天我明白了......我知道有些不对劲......原来只是我哈哈

以上是关于如何仅使用 ForEach 而不是 List 从动态范围中删除的主要内容,如果未能解决你的问题,请参考以下文章

Foreach和doparallel而不是R中的for循环

使用 ForEach SwiftUI Core Data 仅更改列表中的一项

我们如何强制 ForEach 使用 UUID 而不是 (id: \.self)?

如何在面板控件中指定图片框的位置而不是使用foreach?

Java list .remove 方法仅适用于 foreach 循环中的倒数第二个对象[重复]

如何将 foreach 输出为字符串 c# // g 打印为数组而不是字符串