2016-11-08 4 views
0

Я пытаюсь сделать функцию, которую я могу вызвать, чтобы обновить мой UILabel scoreLabel.text. Тем не менее, я получаю сообщение об ошибке, когда я пытаюсь изменить его. Меня смущает то, что я не получаю ошибку при ее изменении внутри viewDidLoad(). Во все остальном возвращает следующее сообщение об ошибке:Изменение текста UILabel работает только внутри viewDidLoad()

Imgur Link

В консоли я также получаю ошибку:

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

Так что я был ведущим верить, что при вызове моей функции, чтобы обновить текст , представление еще не загрузило UILabel. Но я уверен, что эта функция вызывается только после загрузки .

Вещи я проверил/пытались для:

  • Это мой IBOutlet правильно подключен
  • Это моя функция в настоящее время под названием
  • Использование обоих scoreLabel.text и self.scoreLabel.text
  • Использование сильных и слабых выходное соединение

Я также уверен, что changeTextLabel вызывается после того, как scoreLabel загружается в память. Но опять моя ошибка, похоже, говорит иначе.

Вот полная разметка моего кода. Я удалил некоторые ненужные детали для читаемости:

import UIKit 
import SpriteKit 

class GameViewController: UIViewController { 



@IBOutlet weak var scoreLabel: UILabel! 

override func viewDidLoad() { 
    super.viewDidLoad() 
    //This is where all my other code was 
    print(scoreLabel.text) 
    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 
    } 
} 
} 

Спасибо за ваше время

Edit:GameScene.swift

Это только та часть, которая должна быть озабоченность

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

    object.changeTextLabel() 
    projectile.removeFromParent() 
    monster.removeFromParent() 
} 
+0

вы пытались напечатать 'self.scoreLabel', чтобы проверить его ноль или нет? – Tj3n

+0

где вы вызываете метод changeTextLabel от какого-либо другого контроллера? –

+0

Проверить с помощью контрольных точек, которые сначала вызывается viewdidload, или сначала вызывается changetextlabel. – Windindi

ответ

1

Я не полностью знаю вашу настройку проекта, но ваш подход кажется сложным, а не тем, что вы должны делать в играх SpriteKit. Поэтому я не думаю, что это хорошая идея, чтобы рассказать вам, как исправить вашу текущую проблему.

Вы должны создать свой пользовательский интерфейс, например метки, кнопки и т. Д., Используя API-интерфейсы SpriteKit (SKLabelNodes, SKSpriteNodes, SKNodes и т. Д.) Непосредственно в соответствующих SKScene (s).

Ваш GameViewController должен только реально обрабатывать представление SKScenes. поэтому рядом с ним нет кода, кроме загрузки 1-го SKScene.

Если у вас несколько SKScenes (MenuScene, ShopScene, SettingScene и т. Д.), Ваш подход будет разваливаться, потому что метка оценки будет отображаться во всех SKScenes. GameViewController представляет все ваши SKScenes, так что добавлено в GameViewController во всех SKscenes. Это означает, что вам нужно удалить/добавить ярлыки и другой пользовательский интерфейс для каждой сцены, и это будет хаос.

Чтобы создать метку оценки, вы должны сделать это прямо в соответствующей сцене. Мне нравится использовать ленивый подход, чтобы сохранить код установки для метки в том же месте.

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) { 

      addChild(scoreLabel) 
     } 
} 

Чем больше вы можете обновить его прямо в коллизионном коде, который у вас есть.

scoreLabel.text = "NewText" 

Теперь, когда вы переходите от 1 SKScene другого SKScene вам не придется беспокоиться об удалении этой метки, SpriteKit сделает это за вас.

Вы также можете использовать редактор уровней xCodes, чтобы визуально добавить эту метку, аналогичную раскадровке. Это подводит меня к моему окончательному предложению, которое заключается в том, что раскадровки на самом деле не используются в играх SpriteKit.Раскадровки предназначены для ViewControllers, а в SpriteKit вы работаете с SKScenes.

Надеется, что это помогает

0

Это происходит потому, что вы не получаете память метки в какой-либо другой функции. Это происходит из-за так много причин s.

Одна из причин может быть @IBOutlet слабый var scoreLabel: UILabel! Попробуйте создать сильную переменную розетку.

@IBOutlet strong var scoreLabel: UILabel! 

Вторая причина может заключаться в том, что вы вызываете changeTextLabel, прежде чем метка получит память. Поэтому позвоните позже.

+0

Я пробовал использовать сильную и слабую переменную розетку, и changeTextLabel точно называется после того, как метка инициализируется в память. Спасибо за помощь! – EthanBar

+0

Он должен работать и на вас. Он работает для меня. – User511

+0

еще одна вещь добавляет точку останова и проверяет, является ли метка нулевой, тогда сделайте это self.scoreLabel? .text = "yay" – User511

0

Замените эту строку

self.scoreLabel.text = "yay" 

С

self.scoreLabel?.text = "yay" 
0

сделать эти изменения и попробуйте еще раз:

func changeTextLabel() { 
     print("changeTextLabel called") 
     if self.scoreLabel != nil { 
      self.scoreLabel.text = "yay" 
     } 

    } 


func projectileDidCollideWithMonster(projectile: SKSpriteNode, monster: SKSpriteNode) { 
    print("Hit") 
    let game = STORYBOARD.instantiateViewControllerWithIdentifier("STORYBOARD_IDENTIFIER") as! GameViewController 
    game.changeTextLabel() 
    projectile.removeFromParent() 
    monster.removeFromParent() 
} 
+0

Обновлено мое GameScene, чтобы использовать это сейчас, очень полезно знать, что я делал это неправильно! Однако он не исправил мою ошибку. И scoreLabel.text still = nil – EthanBar

+0

Метка не попала в память, если только не загружено представление, вам нужно загрузить представление перед использованием метки. В этом случае вы создаете экземпляр Game View Controller, но его представление не загружено, т. Е. Метка по-прежнему равна нулю. Если вы представляете контроллер viewcontroller или pushviewcontroller, который решит вашу проблему. –

+0

Можете ли вы подробнее рассказать о том, что вы подразумеваете под «настоящим» viewcontroller? – EthanBar

1

Проблема с этой линии,

let game = GameViewController() 
game.changeTextLabel() 

Вы должны создать объект GameViewController с помощью XIB или Storyboard.

В вашем случае это просто создание объекта с использованием .swift-файла, формального класса. Вы должны создать свой объект, используя View, который имеет соединение IBOutlet с scoreLabel.

Try (При использовании раскадровки),

let storyboard = UIStoryboard(name: "YourStoryBoard", bundle: nil) 
let gameVC = storyboard.instantiateViewController(withIdentifier: "GameViewController") as! UIViewController 

или (При использовании XIB)

var gameVC = GameViewController(nibName: "GameViewController", bundle: nil) 

Update вы changeTextLabel со следующими (Ref),

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

Обновлен мой GameScene, чтобы использовать это сейчас, очень полезно знать, что я делал это неправильно! Однако он не исправил мою ошибку. И scoreLabel.text still = nil – EthanBar

+0

@Mathperson Пожалуйста, обновите свой вопрос, какие изменения вы сделали, поэтому я могу подойти поближе, мы на правильном пути. –

+0

Хорошая идея, все! – EthanBar

Смежные вопросы