Ethan Ethan - 1 year ago 58
Swift Question

Changing UILabel's text only works inside viewDidLoad()

I'm trying to make a function that I can call on to update my UILabel's

. However, I get an error whenever I try to change it. What's confusing to me is that I don't receive an error when changing it inside
. Everywhere else returns the following error:

Imgur Link

In the console I also get error:

fatal error: unexpectedly found nil while unwrapping an Optional value.

So what I've been lead to believe is that when calling my function to update the text, the view hasn't loaded the UILabel yet. But I'm certain that this function is called only once the view has loaded.

Things I've checked/tried for:

  • That my IBOutlet is properly connected

  • That my function is being called

  • Using both

  • Using a Strong and Weak outlet connection

I am also positive that
is being called after
is loaded into memory. But again my error seems to say otherwise.

Here's a complete markup of my code. I've removed some irrelevant details for readability:

import UIKit
import SpriteKit

class GameViewController: UIViewController {

@IBOutlet weak var scoreLabel: UILabel!

override func viewDidLoad() {
//This is where all my other code was
scoreLabel.text = "Testing" //This line can be run

func changeTextLabel() {
print("changeTextLabel called")
if self.scoreLabel != nil {
self.scoreLabel.text = "yay"
} else {
print("scoreLabel is nil") //This is called every time

Thanks for your time


This is only the part that should be of concern

func projectileDidCollideWithMonster(projectile: SKSpriteNode, monster: SKSpriteNode) {
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let object = storyBoard.instantiateViewController(withIdentifier: "GameViewController") as! GameViewController


Answer Source

I dont fully know your project setup but your approach seems complicated, its not what you should do in SpriteKit games. So I dont think its a good idea to tell you how to fix your current problem.

You should create your UI, such as labels, buttons etc using SpriteKit APIs (SKLabelNodes, SKSpriteNodes, SKNodes etc) directly in the relevant SKScene(s).

Your GameViewController should only really handle presenting SKScenes. so there should be next to no code there apart from loading the 1st SKScene.

If you have multiple SKScenes (MenuScene, ShopScene, SettingScene etc) your approach will fall apart because the score label will show in all SKScenes. GameViewController presents all your SKScenes, so whats added to GameViewController shows in all SKscenes. That means you have to remove/add labels and other UI for each scene and it will be chaos.

So to create a score label you should do this directly in the relevant scene. I like to use the lazy var approach to keep the setup code for the label in the same spot.

class GameScene: SKScene {

      lazy var scoreLabel: SKLabelNode = {
          let label = SKLabelNode(fontNamed: "HelveticaNeue")
          label.text = "SomeText"
          label.fontSize = 22
          label.fontColor = .yellow
          label.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
          return label

      override func didMove(to view: SKView) {


Than you can update it like this directly in the collision code you have.

scoreLabel.text = "NewText"

Now when you change from 1 SKScene to another SKScene you dont have to worry about removing this label, SpriteKit will do it for you.

You could also use xCodes level editor to add this label visually, similar to storyboards. This brings me to my final suggestion, which is that storyboards are not really used in SpriteKit games. Storyboards are for ViewControllers and in SpriteKit you are working with SKScenes.

Hope this helps