SwiftUI 按钮操作不会更新我的观察对象

Posted

技术标签:

【中文标题】SwiftUI 按钮操作不会更新我的观察对象【英文标题】:SwiftUI Button action does not update my Observed Object 【发布时间】:2021-03-15 12:36:10 【问题描述】:

我正在为棋盘游戏创建记分员。我创建了编号按钮,这些按钮应该通过按钮上的数字来增加我的分数。但是,当按钮被按下时,不会发生任何动作。当我设置一个断点时,我可以告诉按钮正在被调用,但没有发生任何动作——即使我输入了基本元素,例如更改我的玩家名称。你能帮我弄个按钮,按按钮上显示的数字增加“分数”吗?

struct ContentView: View 
@EnvironmentObject var theGame:Game
@ObservedObject var player1: Player
@ObservedObject var player2: Player
@ObservedObject var player3: Player
@ObservedObject var player4: Player
@State var isPlayer1Active: Bool = false
@State var isPlayer2Active: Bool = false
@State var isPlayer3Active: Bool = false
@State var isPlayer4Active: Bool = false

var body: some View 
    VStack 
        AdBannerView()
        HStack
            VStack
                Spacer()
                HStack
                    VStack
                        Button(action:
                            player1.isPlayerActive = true
                            player2.isPlayerActive = false
                            player3.isPlayerActive = false
                            player4.isPlayerActive = false
                            theGame.activePlayer = player1.playerName
                        )
                            PlayerView(player: player1)
                        
                        Button(action:
                            player1.isPlayerActive = false
                            player2.isPlayerActive = true
                            player3.isPlayerActive = false
                            player4.isPlayerActive = false
                            theGame.activePlayer = player2.playerName
                        )
                            PlayerView(player: player2)
                        
                        
                    
                    VStack
                        Button(action:
                            player1.isPlayerActive = false
                            player2.isPlayerActive = false
                            player3.isPlayerActive = true
                            player4.isPlayerActive = false
                            theGame.activePlayer = player3.playerName
                            
                        )
                                
                            PlayerView(player: player3)
                            
                        
                        Button(action:
                            player1.isPlayerActive = false
                            player2.isPlayerActive = false
                            player3.isPlayerActive = false
                            player4.isPlayerActive = true
                            theGame.activePlayer = player4.playerName
                            
                        )
                            PlayerView(player: player4)
                        
                        
                    

                    addPointsCombinedView(player1: Player(), player2: Player(),player3: Player(), player4: Player())
                        .padding()
                        .layoutPriority(1)
                
                .padding()
            
        
    


struct ContentView_Previews: PreviewProvider 
static var previews: some View 
    ContentView(player1: Player(), player2: Player(),player3: Player(), player4: Player())
        .environmentObject(Game(numberOfPlayers: 4))
        .previewDevice("iPad Pro (9.7-inch)")


播放器视图

struct PlayerView: View 
@ObservedObject var player: Player
@EnvironmentObject var theGame:Game;
@State var changeName: Bool = false

var borderSize: CGFloat = 2
var body: some View 
    
    HStack
        Text(player.playerName)
            .foregroundColor(.white)
            .padding()
        VStack
            Text("Score")
            Text(player.formattedScore)
        
        .foregroundColor(.white)
        .padding()
        
    
    .background(player.isPlayerActive ? Color.blue : Color.green)
    .cornerRadius(25)
    
    
    
    
    




/* Extension that allows custom corner radius for each corner.  This code was created by Mojtaba Hosseini and found on *** https://***.com/questions/56760335/round-specific-corners-swiftui*/

/*    extension View 
func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View 
    clipShape(playerRoundedCornerView(radius: radius, corners: corners) )

*/
/*This code was created by Mojtaba Hosseini and found on ***     https://***.com/questions/56760335/round-specific-corners-swiftui*/
struct playerRoundedCornerView: Shape 

var radius: CGFloat = .infinity
var corners: UIRectCorner = .allCorners

func path(in rect: CGRect) -> Path 
    let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii:   CGSize(width: radius, height: radius))
    return Path(path.cgPath)


struct PlayerView_Previews: PreviewProvider 
static var previews: some View 
    PlayerView(player: Player())


加分按钮

struct addScoreButton: View 
@ObservedObject var player1: Player
@ObservedObject var player2: Player
@ObservedObject var player3: Player
@ObservedObject var player4: Player
@EnvironmentObject var theGame:Game

var numberToAdd:String
var body: some View 
    Button(action: 
            player1.changePlayerName(newPlayerName: "Hello2")
        switch theGame.activePlayer
        case player1.playerName:
            player1.addToScore(pointToAdd: addScore(numberToAdd: numberToAdd))
            
        case player2.playerName:
            player2.addToScore(pointToAdd: addScore(numberToAdd: numberToAdd))
        default:
            theGame.activePlayer = "hello world"
        )
    ZStack
        Rectangle()
            .fill(Color.gray)
            //.frame(width: 75, height: 75, alignment: .center)
            .frame(minWidth: 70, idealWidth: 75, maxWidth: 100, minHeight: 70, idealHeight: 75, maxHeight: 100, alignment: .center)
            .padding(.bottom, 5)
            
        
        Text("\(numberToAdd)")
            .foregroundColor(.white)
            .bold()
            .font(.title)
            
    






struct addScoreButton_Previews: PreviewProvider 
static var previews: some View 
    addScoreButton(player1: Player(), player2: Player(),player3: Player(), player4: Player(),numberToAdd: "+1")



func addScore(numberToAdd:String) -> Int 
    switch numberToAdd 
    case "+1":
        return 1
    case "+2":
        return 2
    case "+3":
        return 3
    case "+4":
        return 4
    case "+5":
        return 5
    case "+6":
        return 6
    case "+12":
        return 12
    case "-1":
        return -1
    case "-2":
        return -2
    default:
        return -3
    

按钮的组合视图:

struct addPointsCombinedView: View 
@ObservedObject var player1: Player
@ObservedObject var player2: Player
@ObservedObject var player3: Player
@ObservedObject var player4: Player
@EnvironmentObject var theGame:Game
var one:String = "+1"
var two:String = "+2"
var three:String = "+3"
var four:String = "+4"
var five:String = "+5"
var six:String = "+6"
var twelve:String = "+12"
var negOne:String = "-1"
var negTwo:String = "-2"


var body: some View 
    HStack 
        VStack
            addScoreButton(player1: player1, player2: player2,player3: player3, player4: player4, numberToAdd: one)
            addScoreButton(player1: player1, player2: player2,player3: player3, player4: player4,numberToAdd: four)
            addScoreButton(player1: player1, player2: player2,player3: player3, player4: player4,numberToAdd: twelve)
            
        
        .padding(.trailing, 3)
        VStack
            addScoreButton(player1: player1, player2: player2,player3: player3, player4: player4,numberToAdd: two)
            addScoreButton(player1: player1, player2: player2,player3: player3, player4: player4,numberToAdd: five)
            addScoreButton(player1: player1, player2: player2,player3: player3, player4: player4,numberToAdd: negOne)

        
        VStack
            addScoreButton(player1: player1, player2: player2,player3: player3, player4: player4,numberToAdd: three)
            addScoreButton(player1: player1, player2: player2,player3: player3, player4: player4,numberToAdd: six)
            addScoreButton(player1: player1, player2: player2,player3: player3, player4: player4,numberToAdd: negTwo)
        
        .padding(.leading, 3)
    
    



struct addPointsCombinedView_Previews: PreviewProvider 
static var previews: some View 
    addPointsCombinedView(player1: Player(), player2: Player(),player3: Player(), player4: Player())


玩家对象:

class Player:ObservableObject 
@Published var playerName:String
@Published var score:[GameRound] = []
@Published var isPlayerPlayingThisGame:Bool
@Published var isPlayerActive:Bool
@Published var currentScore: Int
var formattedScore:String
    String(currentScore)


//@Published let currentRound:Int
//@Published var beginningScore:Int
//@Published var endScore:Int
//@Published var pointsThisRound:[Int]


init()
    playerName = "Player1"
    let newGame = GameRound(newRound: 1,beginningScore: 0 )
    isPlayerActive = false
    isPlayerPlayingThisGame = false
    currentScore = 0
    score.append(newGame)
    

func changePlayerName(newPlayerName: String)
    playerName = newPlayerName

/*adds the new points to the array of points made this round
and adds them to the current score */
func addToScore(pointToAdd: Int)
    //score.last?.addScore(pointsToAdd: pointToAdd)
    self.currentScore += pointToAdd
    score.last?.endScore += pointToAdd
    score.last?.pointsThisRound.append(pointToAdd)
    


/*starts a new round of the game*/
func newRound()
    let newRound = GameRound(newRound:self.score.last!.currentRound+1,beginningScore: self.score.last!.endScore)
    self.score.append(newRound)


GameRoundObject

class GameRound
let currentRound:Int
var beginningScore:Int
var endScore:Int
var pointsThisRound:[Int]
var scoreAsString:StringString(endScore)

init(newRound:Int, beginningScore: Int)
    self.currentRound = newRound
    self.beginningScore = beginningScore
    self.endScore = newRound
    self.pointsThisRound = [0]


func addScore(pointsToAdd:Int)
    self.endScore += pointsToAdd
    self.pointsThisRound.append(pointsToAdd)


func roundAsString()->String
    var roundNumber:String
    var round = "Round:"
    roundNumber = "\(self.currentRound)"
    round += roundNumber
    return round

游戏对象

class Game:ObservableObject
@Published var player1 = Player()
@Published var player2 = Player()
@Published var player3 = Player()
@Published var player4 = Player()
@Published var activePlayer:String
@Published var game:[Player] = []
@Published var currerntRound: Int

init(numberOfPlayers: Int)
    activePlayer = " "
    currerntRound = 1
    for i in 1...numberOfPlayers
        switch i 
        case 1:
            game.append(player1)
            player1.isPlayerPlayingThisGame = true
        case 2:
            game.append(player2)
            player2.isPlayerPlayingThisGame = true
        case 3:
            game.append(player3)
            player3.isPlayerPlayingThisGame = true
        case 4:
            game.append(player4)
            player4.isPlayerPlayingThisGame = true
        default:
            break
        
    




【问题讨论】:

【参考方案1】:

您当前有一行正在创建新的 Player 实例,而不是引用已经创建的实例。

您的线路:

addPointsCombinedView(player1: Player(), player2: Player(),player3: Player(), player4: Player())

应该是:

addPointsCombinedView(player1: player1, player2: player2,player3: player3, player4: player4)

以上将解决您的问题。但是,总的来说,我还要说,在架构上,我不确定将所有玩家存储在Game 环境变量中是否有意义,该环境变量会发送到每个视图传递每个玩家也可以作为@ObservableObject 参数。

【讨论】:

这很有效,谢谢。您对 Game 环境对象是正确的。坦率地说,这是我需要清理的旧代码,我只是在尝试新的东西。另一方面,有没有办法让我回答这个问题,以便我给你信用? 有。您可以单击绿色复选标记接受,然后单击向上箭头将其标记为有用。谢谢!

以上是关于SwiftUI 按钮操作不会更新我的观察对象的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI - 从 Swift 类启动的可观察对象不会更新 ContentView() 上的 @ObservedObject

为啥可观察对象中的更新变量不会更新视图?

已发布/观察到的 var 在视图 swiftui 中未更新,带有调用函数

在 SwiftUI 中使用可观察对象切换视图

SwiftUI 视图不会为具有子类的已发布对象更新

嵌套 ObservedObject 中的更改不会更新 UI