【问题标题】:Unable to remove UIButtons and Labels.无法删除 UIButton 和标签。
【发布时间】:2016-01-21 23:54:49
【问题描述】:

我的 Swift 游戏中的 UIButtons 和 UILabels 遇到了一个奇怪的问题。用户在游戏中开始,一旦失败,就会进入游戏结束屏幕。在此游戏结束屏幕上,显示了他们的高分以及返回主菜单或返回玩游戏的两个按钮。这就是问题出现的地方。随机地,每当场景从游戏结束场景切换回游戏场景时,游戏结束场景的按钮和标签有时不会消失。它们作为它们作为游戏其余部分的叠加层的静态表示。

我不知道是什么导致它们大部分时间消失,然后随机决定留在不同的时间。

这是游戏结束场景的代码。到目前为止,我已经尝试使用 .removeFromSuperview()、.alpha = 0 以及现在使用 .hidden = true 的方式删除按钮和标签。这些都不是100%的工作。我已经尝试了所有我能想到的方法,但我不知道是什么导致标签和按钮粘在上面。

import Foundation
import SpriteKit
import UIKit
class endScene : SKScene {
    var RestartBtn : UIButton!
var scoreLabel = UILabel()
var highLabel = UILabel()
var menuButton = UIButton()
var Highscore : Int!
var HS : Int!
override func didMoveToView(view: SKView) {
    RestartBtn = UIButton(frame: CGRect(x: 0, y: 0, width: view.frame.size.width/3, height: 30))
    RestartBtn.center = CGPoint(x: view.frame.size.width / 2, y: view.frame.size.width/1.3)
    RestartBtn.setTitle("Restart", forState: UIControlState.Normal)
    RestartBtn.setTitleColor(UIColor.redColor(), forState: UIControlState.Normal)
    RestartBtn.addTarget(self, action: Selector("Restart"), forControlEvents: UIControlEvents.TouchUpInside)
    menuButton = UIButton(frame: CGRect(x: 0, y: 0, width: view.frame.size.width/3, height: 30))
    menuButton.center = CGPoint(x: view.frame.size.width/2, y: view.frame.size.width/4)
    menuButton.setTitle("Main Menu", forState: UIControlState.Normal)
    menuButton.setTitleColor(UIColor.redColor(), forState: UIControlState.Normal)
    menuButton.addTarget(self, action: Selector("gotoMenu"), forControlEvents: UIControlEvents.TouchUpInside)
    self.view?.addSubview(menuButton)
    self.view?.addSubview(RestartBtn)
    self.scene?.size = CGSize(width: 640, height: 1136)
    let bgImage = SKSpriteNode(imageNamed: "Background")
    bgImage.zPosition = -1
    bgImage.position = CGPointMake(self.size.width/2, self.size.height/2)
    self.addChild(bgImage)
    let ScoreDefault = NSUserDefaults.standardUserDefaults()
    let Score = ScoreDefault.valueForKey("Score") as! NSInteger
    let HighscoreDefault = NSUserDefaults.standardUserDefaults()
    Highscore = HighscoreDefault.valueForKey("Highscore") as? NSInteger
    if (Highscore != nil){

        HS = Highscore
    }
    else {

        HS = 0
    }
    scoreLabel.center = CGPoint(x: view.frame.size.width / 9, y: view.frame.size.width / 4)
    scoreLabel.textColor = UIColor.redColor()
    scoreLabel.font = UIFont.boldSystemFontOfSize(15)
    scoreLabel.font = UIFont(name: "Avenir-Light" , size: 90)
    scoreLabel.text = "\(Score)"
    scoreLabel.sizeToFit()
    highLabel.center = CGPoint(x: view.frame.size.width / 9, y: view.frame.size.width / 2)
    highLabel.textColor = UIColor.redColor()
    highLabel.font = UIFont.boldSystemFontOfSize(15)
    highLabel.font = UIFont(name: "Avenir-Light" , size: 60)
    highLabel.text = "\(HS)"
    highLabel.sizeToFit()
    self.view?.addSubview(scoreLabel)
    self.view?.addSubview(highLabel)
}
func Restart(){
    running = true
    clearendScene()
    changeSceneGame()
}
func clearendScene(){
    menuButton.hidden = true
    scoreLabel.hidden = true
    highLabel.hidden = true
    RestartBtn.hidden = true
}
func changeSceneGame(){
    self.scene!.view!.presentScene(GameplayScene(), transition: SKTransition.crossFadeWithDuration(0.8))
}
func changeSceneMenu(){
    self.scene!.view?.presentScene(GameScene(), transition: SKTransition.crossFadeWithDuration(0.8))
}
func gotoMenu(){
    clearendScene()
    changeSceneMenu()
}

}

This is a screenshot of the overlay bug happening.

This is a screenshot of how the game is supposed to look on the gameplay scene.

【问题讨论】:

    标签: ios xcode swift


    【解决方案1】:

    根据经验,我可能会建议您尽快隐藏这些按钮。我注意到您将它们隐藏在clearedScene 方法中,并将该方法嵌入到restart 和goToMenu 中。但是,您调用 clearScene 的位置可能在您调用转换之前就应该是正确的。

    例如:

            func changeSceneGame(){
        clearEndScene()
        self.scene!.view!.presentScene(GameplayScene(), transition:       SKTransition.crossFadeWithDuration(0.8))
    }
    

    【讨论】:

    • 我刚试过。它没有用。这是一个奇怪的错误,我看不到任何东西可能导致它。
    • 既然您使用的是精灵套件,为什么不使用您正在使用的那些图像作为按钮,而是将它们创建为 SKLabel 节点。这样当用户触摸该特定按钮时,您就可以取消该特定 SKlabel 的父级
    【解决方案2】:

    是否有可能您的 endScene 出现了两次(或者 didMoveToView 出于某种原因被调用了两次)?

    按照您的代码编写方式,endScene 的第二次展示将创建一组新的按钮并失去对它们的跟踪,只留下持有对它们的引用的视图。

    解决此问题的一种懒惰方法是隐藏 didMoveToView 开头的两个按钮。由于您正在创建变量的空实例,因此即使是第一次也应该可以使用。

    (您确实意识到每次呈现 endScene 时都会泄漏这些按钮,对吗?)

    [编辑]

    我猜这个懒惰​​的方式行不通,因为第一次, UIButton() 的实例没有连接到任何东西。

    您需要不要偷懒并修复设计。

    1) 您的按钮变量不应在其声明中创建空实例。

    2) 您不应该在每次 didMoveToView 时都创建按钮的新实例,除非您在某个时间点将它们从视图中移除(见下文)。您的代码每次都将新按钮对象添加到视图中,并且从不释放它们。

    3) 当您执行 RestartBtn = UIButton(frame: CG... 您正在丢失对添加到视图中的上一个按钮的引用。这就是为什么第二次调用 didMoveToView 而不通过 Restart() 或 gotoMenu () 将显示按钮对象。

    4a) 如果您想继续在 didMoveToView() 中添加按钮,请使用 removeFromSuperview() 覆盖 func willMoveFromView() 将它们从超级视图中删除。

    4b) 如果您只想添加它们一次并且只使用它们的隐藏属性来显示/隐藏它们。使您的按钮变量静态,将它们初始化为 nil 并且只创建和添加它们一次(即当调用 didMoveToView() 时变量为 nil 时)。

    注意:由于某些东西会两次显示您的 endScene,因此所有这些都可能没有实际意义,并且可能无法解决问题。也许您应该在 didMoveToView 的开头设置一个断点并检查第一次和第二次调用的调用堆栈。这应该可以揭示第二个调用的来源(或者它可能是第一个无关的)。

    【讨论】:

    • 在尝试隐藏按钮之前,我使用了 removefromSuperview() 但这导致了同样的问题。我刚刚尝试了您描述的懒惰方式,它在第一个 RestartBtn.hidden = true 时给了我一个 EXC_BAD_INSTRUCTION 错误。我很困惑,如果它只是随机发生的话,它怎么可能是 endScene 的第二次展示。
    【解决方案3】:

    我在游戏中遇到了类似的问题。所以我设置了约束,初始化了我的场景(有UIButton)并将它呈现在override func viewWillLayoutSubviews()中。由于某种原因,这个函数被调用了两次。正如 Alain T. 所说,因此存在内存泄漏。

    为了解决这个问题,我将所有用于初始化场景的代码移至 viewDidLoad() 并将 override func viewWillLayoutSubviews() 更改为

    override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() }

    它奏效了!按钮不见了!

    【讨论】:

      猜你喜欢
      • 2020-08-15
      • 2017-04-23
      • 2018-07-22
      • 1970-01-01
      • 1970-01-01
      • 2017-01-06
      • 2014-10-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多