SpriteKit 在转换到新场景时收到有关 UIElements 的错误

Posted

技术标签:

【中文标题】SpriteKit 在转换到新场景时收到有关 UIElements 的错误【英文标题】:SpriteKit getting error regarding UIElements when transitioning to new scene 【发布时间】:2015-11-12 17:01:39 【问题描述】:

您好,我正在使用 Swift 开发一个 SpriteKit 游戏,该游戏现在只有两个场景,一个加载场景和游戏场景。游戏场景在呈现之前访问数据库以获取信息。游戏场景中的一切都运行良好。当我先进入加载场景而不是直接进入游戏场景时,我遇到了问题。通过回调,我告诉加载场景在完成数据库操作后呈现游戏场景。然而,当我尝试展示时,我得到了 10 多个这样的巨大错误:

2015-11-12 09:48:33.880 SpaceAcademy[2373:764586] 此应用程序是 从后台线程修改自动布局引擎,这可以 导致引擎损坏和奇怪的崩溃。这将导致 未来版本中的异常。 (后面是一堆随机信息)

现在通过一些测试,我发现是我的UIElement 导致了这种情况,例如按钮、滑块和标签。但是我将它们移到了didMoveToView 函数中,所以我很困惑为什么它们仍然在抛出错误,因为它们在“后台线程”中..

还有一点奇怪的是,这些并不是致命错误。我的控制台刚刚爆炸,延迟 10 秒后出现 UIElements。其他奇怪的是,如果我点击我知道按钮应该是的位置,该函数仍然会被调用,就好像按钮实际上在那里一样。

我将非常感谢一个解决控制台爆炸的解决方案,但更重要的是摆脱 UI Elements 出现的十秒延迟,谢谢!!

代码设置场景

let skView = view as! SKView
    skView.showsFPS = false
    skView.showsNodeCount = false
    skView.ignoresSiblingOrder = true

    let scene = GameScene(size: view.bounds.size)
    scene.scaleMode = SKSceneScaleMode.ResizeFill


    let loading = LoadingScene(size: view.bounds.size, newScene: scene)
    skView.presentScene(loading, transition: SKTransition.crossFadeWithDuration(1))

    scene.setEverythingUp(loading)

加载场景:

import Foundation
import SpriteKit

class LoadingScene:SKScene
    var newScene:SKScene!
    var oldCount = 0
    var loadCount = 0
    didSet
        print(loadCount)
        if loadCount == 0
            if oldCount == 1
                self.view?.presentScene(newScene)
            
        
        oldCount = loadCount
    


init(size: CGSize, newScene:SKScene) 
    self.newScene = newScene
    super.init(size: size)



required init?(coder aDecoder: NSCoder) 
    fatalError("init(coder:) has not been implemented")


override func didMoveToView(view: SKView) 
    self.backgroundColor = UIColor.blackColor()
    let imageArray = [
        SKTexture(imageNamed: "Loader/Loader1.png"),
        SKTexture(imageNamed: "Loader/Loader2.png"),
        SKTexture(imageNamed: "Loader/Loader3.png")
    ]

    let loader = SKSpriteNode(texture: imageArray[0])
    let animation = SKAction.animateWithTextures(imageArray, timePerFrame: 0.33)
    loader.runAction(SKAction.scaleBy(0.25, duration: 0))
    loader.zPosition = 100
    loader.runAction(SKAction.repeatActionForever(animation))
    loader.position = CGPointMake(self.size.width/2, self.size.height/2)
    self.addChild(loader)


func start()
    self.loadCount = self.loadCount + 1


func end()
    self.loadCount = self.loadCount - 1



来自 GameScene 的代码:

func setEverythingUp(myLoadingScene: LoadingScene)

    let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()

    var Id = 0
    if let tempID = defaults.objectForKey("UserId") as? Int 
       Id = tempID
    else
        print("newUser")
        defaults.setObject(0, forKey: "UserId")
        defaults.synchronize()
    

    self.mySettings = Settings(UserId: Id)
    self.myURLRequests = URLRequests(thesettings: self.mySettings)

    ///////////call back system here, start() increments number of tasks waiting to finish, end() means a task is finished

    myLoadingScene.start()
    myURLRequests.getFleet(self, UserId: self.mySettings.UserId, enemy: false) () -> () in
        myLoadingScene.end()
    

    myLoadingScene.start()
    myURLRequests.getFleet(self, UserId: self.mySettings.UserId, enemy: true) () -> () in
        myLoadingScene.end()
    



    self.backgroundColor = SKColor.blackColor()
    self.physicsWorld.contactDelegate = self
    self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
    addStars()


    self.anchorPoint = CGPoint(x:self.size.width/2, y:self.size.height/2)
    self.myCamera.position = CGPointMake(200, 0)
    self.addChild(self.myCamera)
    self.camera = self.myCamera
    self.myAnchor.position = self.myCamera.position




override func didMoveToView(view: SKView) 
    print("in view")

    let pinch = UIPinchGestureRecognizer(target: self, action: Selector("PinchScale:"))
    let tap = UITapGestureRecognizer(target: self, action: Selector("Tap:"))
    let pan = UIPanGestureRecognizer(target: self, action: Selector("DragMove:"))

    self.view?.addGestureRecognizer(pinch)
    self.view?.addGestureRecognizer(tap)
    self.view?.addGestureRecognizer(pan)


    let fireButton = UIButton(type: .System)
    fireButton.setTitle("Fire!", forState: UIControlState.Normal)
    fireButton.titleLabel!.font = UIFont(name: "TrebuchetMS", size: 70)
    fireButton.sizeToFit()
    fireButton.frame = CGRect(x: self.size.width*0.5 - 80, y: 20, width: self.size.width*0.3, height: 30)
    fireButton.addTarget(self, action: Selector("FireButtonClicked"), forControlEvents: UIControlEvents.TouchUpInside)
    self.view?.addSubview(fireButton)

    let alternateControls = false

    let AngleLabel = UILabel()
    AngleLabel.text = "Angle"
    AngleLabel.font = UIFont(name: "TrebuchetMS", size: 30)
    AngleLabel.textColor = UIColor.blueColor()


    let AngleSlider = UISlider()
    AngleSlider.addTarget(self, action: Selector("AngleSlided:"), forControlEvents: UIControlEvents.ValueChanged)
    AngleSlider.maximumValue = 90
    AngleSlider.minimumValue = -90
    AngleSlider.value = 0
    AngleSlider.continuous = true



    let PowerLabel = UILabel()
    PowerLabel.text = "Power"
    PowerLabel.font = UIFont(name: "TrebuchetMS", size: 30)
    PowerLabel.textColor = UIColor.blueColor()


    let PowerSlider = UISlider()
    PowerSlider.addTarget(self, action: Selector("PowerSlided:"), forControlEvents: UIControlEvents.ValueChanged)
    PowerSlider.maximumValue = 0.99
    PowerSlider.minimumValue = 0.1
    PowerSlider.value = 0.5
    PowerSlider.continuous = true


    if alternateControls
        AngleLabel.frame = CGRect(x: self.size.width*0.05, y: 10, width: self.size.width*0.3, height: 60)
        AngleSlider.frame = CGRect(x: self.size.width * -0.05, y: self.size.height*0.55, width: self.size.height*0.7, height: 30)
        AngleSlider.transform = CGAffineTransformMakeRotation(CGFloat(M_PI) * 1.5)
        PowerLabel.frame = CGRect(x: self.size.width*0.7, y: 10, width: self.size.width*0.3, height: 60)
        PowerSlider.frame = CGRect(x: self.size.width * 0.65, y: self.size.height*0.55, width: self.size.height*0.7, height: 30)
        PowerSlider.transform = CGAffineTransformMakeRotation(CGFloat(M_PI) * 1.5)
    else
        AngleLabel.frame = CGRect(x: self.size.width*0.21, y: self.size.height-80, width: self.size.width*0.3, height: 60)
        AngleSlider.frame = CGRect(x: self.size.width * 0.025, y: self.size.height-30, width: self.size.width*0.45, height: 30)
        PowerLabel.frame = CGRect(x: self.size.width*0.71, y: self.size.height-80, width: self.size.width*0.3, height: 60)
        PowerSlider.frame = CGRect(x: self.size.width * 0.525, y: self.size.height-30, width: self.size.width*0.45, height: 30)
    

    self.view?.addSubview(AngleLabel)
    self.view?.addSubview(AngleSlider)
    self.view?.addSubview(PowerLabel)
    self.view?.addSubview(PowerSlider)


【问题讨论】:

移除 UIElements 移除了一半的错误,但仍有大约 8 个左右 什么是 UIElement?我在您发布的代码中没有看到对 UIElement 的任何引用。 AutoLayout 是一个 UIKit 功能,你展示的是 SpriteKit。 UIButton、UILabel 等 好的,这些被称为 UIViews。发布您的 UIKit 代码。您发布了引用 SpriteKit 的代码,并且没有生成自动布局警告。特别是,您是否在后台线程中添加或删除 UIViews?你在哪里有后台线程?也许在网络响应处理程序中? “通过回调我告诉加载场景在完成数据库操作后呈现游戏场景。”让我们看看这些回调以及它们是如何被调用的。很有可能这就是您的问题所在。 【参考方案1】:

你正在这样做:

myURLRequests.getFleet(self, UserId: self.mySettings.UserId, enemy: false) () -> () in
    myLoadingScene.end()

一旦 URL 请求完成,它可能会在后台线程上调用 myLoadingScene.end()end 设置loadCount,它有一个启动新视图的setter,self.view?.presentScene(newScene)

这是需要在主线程上运行的最后一部分。

假设你有一个方便的函数可以在主线程上运行一些东西:

func on_main_async (closure:()->()) 
    dispatch_async(dispatch_get_main_queue()) 
        closure()
    

然后你可以将场景演示代码更改为

on_main_async()  self.view?.presentScene(newScene) 

应该做的伎俩。

【讨论】:

太棒了!工作完美!只需将“newScene”更改为“self.newScene” 太棒了!很高兴它奏效了!我希望您现在对 UIKit/autolayout 和 SceneKit 之间的区别有了更好的了解。

以上是关于SpriteKit 在转换到新场景时收到有关 UIElements 的错误的主要内容,如果未能解决你的问题,请参考以下文章

spritekit 场景转换工作不正确

转换场景时出错? - 精灵套件

bundleID、SpriteKit 没有捆绑包

iOS Spritekit 游戏中的肌酐水平

是否可以使用SpriteKit在应用程序上使用Xcode UI Testing?

SpriteKit:你如何突出场景的一部分,就像在教程中一样?