使用 MainView 布尔值调用 Subview 函数

Call Subview function with MainView boolean

我正在拼命尝试以下操作:

我在 MainView 中有一个 ForEach 循环,其中包含不同数量的 ListRowView。此 ListRowView 包含一个带 player.name 和 player.points 的 HStack 以及一个 TextField,玩家可以在其中写入一个数字,然后将其相加或相减。

在按下 MainView 中的“计算”按钮后的 ListRowView 中,应该调用函数 updatePoints(),但在这里我失败了(我用 didSet 和 willSet 试过了)。这里有人知道如何最好地实现它吗?

谢谢

struct MainView: View {
    
    //Binding
    @Binding var game : Game

    //for Changing the Points
    @State private var addingPoints : Bool = true
    @State private var invokeFunction : Bool = false
    
    var body: some View {
        
        VStack{
            
            let players : [Player] = game.players
            
            NavigationView{
                
                VStack{
                    
                    List{
                        
                        Section(){
                            
                            ForEach(players, id: \.self){
                                player in
                                
                                ListRowView(invokeFunction: $invokeFunction, addingPoints: addingPoints, player: player)
                            }
                        }
                        
                        Button(action: {
                            invokeFunction.toggle()
                        }, label: {
                            Text("Calculate")
                            }
                        })
                        
                    }
                }
            }
        }
    }
    
}

这里是(子视图)ListRowView,应该在其中调用 func updatePoints()。

struct ListRowView: View {
    
    @Binding var invokeFunction : Bool 
    
    @State var addingPoints : Bool
    @State var player : Player
    
    var body: some View {
        
        HStack{
            
            Text("\(player.name)")
            
            Spacer()
            
            Text("\(player.points)")
            
            Spacer()
            
            
            //add or delete number from points
            Button(action: {
                addingPoints.toggle()
            }){
                if(addingPoints){
                    Image(systemName: "plus.circle.fill")
                        .foregroundColor(.green)
                } else {
                    Image(systemName: "minus.circle.fill")
                        .foregroundColor(OwnColor.ownRed)
                }
            }.buttonStyle(BorderlessButtonStyle())
            
            
            //added new points with own TextField
            TextField("0", text: $limitTextField.text)
                .keyboardType(.numberPad)
            
        }
    }
    
    mutating func updatePoints(add: Bool) -> Void {
        var newPoints = 0
        
        if self.invokeFunction {
            if (limitTextField.text.isEmpty) {
                self.player.points += newPoints
            } else {
                newPoints = Int(limitTextField.text) ?? 0
                if (add) {
                    self.player.points += newPoints
                    print("Add \t Old points: \(player.points - newPoints) - New Points: \(player.points)")
                } else {
                    self.player.points -= newPoints
                    print("Sub \t Old points: \(player.points + newPoints) - New Points: \(player.points)")
                }
                
            }
        }
        
        invokeFunction.toggle()
    }
    
}

这里也是 Player 结构体

struct Player : Identifiable, Codable, Hashable{
    let id : UUID
    var name : String
    var points : Int
    
    var hashValue: Int {
        return name.hashValue
    }
}

在您的 MainView 中,只需将 @Binding 与游戏变量一起使用

var body: some View {
    
    //Remove this
    //let players : [Player] = game.players
    NavigationView{                
        VStack{   
            List{                        
                Section(){                            
                    ForEach($game.players, id: \.self){
                        player in
                        
                        ListRowView(invokeFunction: $invokeFunction, addingPoints: addingPoints, player: player)
                    }
                }
                
                Button(action: {
                    calculateAllPlayers()
                }, label: {
                    Text("Calculate")
                    }
                })
                
            }
        }
    }
    
}

这样,每当 'game' 对象在 calculateAllPlayers 函数中被修改时,ForEach 就会动态地更新玩家,您将得到类似这样的东西

func calculateAllPlayers(add: Bool) -> Void {

    for player in game.players {
        //do your calulations here modifying the 'player' object
    }
    
}

在您的子视图中,您只需从播放器变量中删除@State 并添加@Binding

编辑:我在这里添加另一种可能性,因为 OP 指定他无法从主视图访问数据。

你也可以在子视图上添加一个 .onChange(of)

HStack{
    
    Text("\(player.name)")
    //....
    
}
.onChange(of: invokeFunction) {
    if invokeFunction {
       //call your function
    }
}

每次 of 状态改变时都会调用 onChange