Search code examples
swiftsprite-kittvos

Mutating UILabels in the ViewController from another unrelated class


I've been working on a score and highscore display for my tvOS sprite kit game. I have two classes. One of the classes is called "GameViewController," a UIViewController, and the other is a subclass of SKSpriteNode for my player. What I want to do is for every five updates, the score will update. The labels for the score and highscore are accessed via variables in my GameViewController class. In my Player class is where the updates are done, so I need to trigger a method in my GameViewController class from my Player class.

I've tried a few things. The furthest I've gotten is with this method using "Protocols" and "Delegates." I have a protocol class called "ScoreUpdateResponder" with a method:

scoreUpdate(let score: Int)

Thus, my GameViewController implements(? I'm a Java dev, trying to connect the dots here) the Responder class. So my cut-down class structure looks like this:

Player Class:

class Player: SKSpriteNode
{
    weak var responder: ScoreUpdateResponder?

    init(responder : ScoreUpdateResponder) 
    {
        self.responder = responder
    }

    let score = 0

    func update()
    {
      if(score % 5 == 0)
      {
        responder?.updateScore(score / 5)
      }

      score++;
    }
}

** ScoreUpdateResponder class**

protocol ScoreUpdateResponder : class
{
    func updateScore(let score: Int)

}

And then my GameViewController is:

class GameViewController: UIViewController, ScoreUpdateResponder
{
  func updateScore(score: Int)
  {
    scoreValue.text = String(score)

    if(score > Int.init(highscoreValue.text!)!)
    {
      highscoreValue.text = String(score)
    }
  }
}

I know I must be missing something. I suspect its with my initialization of the responder, or more generally in the Player class. Any help?


Solution

  • I'm going to make a few assumptions here.

    Assumptions:

    • There is only one player in this game, that's represented by a singleton
    • Something not shown is responsible for calling player.Update()

    I think the issue that you might be running into is the let score = 0 line. I think what you're looking to do is actually: var score = 0. let effectively defines a constant.

    I also edited some of the code to make it more inline with common swift style guides.

    class GameViewController: UIViewController {
    
        @IBOutlet private weak var scoreValue: UILabel!
        @IBOutlet private weak var highscoreValue: UILabel!
    
        var player = Player()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            player.delegate = self
        }
    
        ...
    
    }
    
    // MARK: PlayerDelegate
    extension GameViewController: PlayerDelegate {
    
        func playerDidUpdateScore(score: Int) {
            self.scoreValue.text = String(score)
    
            guard let highScoreText = highscoreValue.text, highScore = Int(highScoreText) where score > highScore else {
                    return
            }
    
            self.highscoreValue.text = String(score)
        }
    }
    
    protocol PlayerDelegate {
        func playerDidUpdateScore(score: Int)
    }
    
    class Player: SKSpriteNode {
    
        var delegate: PlayerDelegate?
        static var score = 0
    
        func update() {
            if self.score % 5 == 0 {
                self.delegate?.playerDidUpdateScore(self.score / 5)
            }
    
            self.score++;
        }
    }