在“applicationDidFinishLaunching”方法退出之前,SpriteKit 窗口不会重绘
Posted
技术标签:
【中文标题】在“applicationDidFinishLaunching”方法退出之前,SpriteKit 窗口不会重绘【英文标题】:SpriteKit windows do not redraw until "applicationDidFinishLaunching" method quits 【发布时间】:2015-01-15 13:59:47 【问题描述】:我使用 Mac OS X(不是 ios)的 SpriteKit 来运行我的程序。
在“AppDelegate”类的“applicationDidFinishLaunching”方法结束时,我启动了初始化所需的所有内容。有些方法不喜欢从后台线程调用,例如设置窗口标题、调整窗口大小和其他一些任务。所以所有这些事情都是在主线程中完成的。
然后我们来解决我的问题:我不能简单地在“applicationDidFinishLaunching”方法结束时运行我的主程序,因为当我这样做时,“applicationDidFinishLaunching”方法在我的主程序退出之前不会退出。而且我的主程序并没有退出,因为它在启动程序后直接在屏幕上显示了一些动画。
在“applicationDidFinishLaunching”方法没有退出的情况下,SpriteKit 不会重绘窗口,所以我的动画运行但我看到一个白色窗口。
退出我的程序后,“applicationDidFinishLaunching”方法也退出了,我看到了动画的最后一张图片。
所以我想到了一个解决方法:我现在在“applicationDidFinishLaunching”方法中进行初始化,然后启动一个运行我的主程序的后台线程。
“applicationDidFinishLaunching”在启动后台线程后退出,窗口按预期更新。后台线程执行动画时一切正常。
现在的问题,我无法解决:我需要隐藏菜单栏,不是在启动程序时直接隐藏,而是在一段时间后。
NSMenu.setMenuBarVisible(false)
从主线程调用时这样做没有问题,但是如果我从后台线程中隐藏菜单栏,那么我可以将其隐藏一次,使其可见一次,再次隐藏它并使其可见AppDelegate 类中的异常再次停止我的程序:
Thread 1: EXC_BAD_ACCESS (code=EXC_i386_GPFLT)
我解决这个问题的想法是发布一个由主线程处理的事件。但是,例如,如果我发布一个键盘事件,事件处理也是在后台线程中完成的。
诸如用户选择菜单之类的事件,不是以编程方式从主线程处理的,但我没有找到一种方法来发布事件,然后在主线程而不是包含 sendEvent-command 的线程中处理该事件:
NSApplication.sharedApplication().sendEvent(event!) // Called from background-thread
有谁知道发送一个由主线程处理的事件
或
在主线程中完全运行我的程序而不会出现窗口内容根本没有绘制的问题。第二个解决方案是我最喜欢的,因为还有一些事情会在后台线程中产生问题。
也许我可以在“applicationDidFinishLaunching”完成一段时间后从另一个方法启动我的主程序。
关于上述主题的一些更深入的信息,但仍然没有解决方案:
我发现,有一个函数“performSelectorOnMainThread”可以像这样从 swift 调用:
NSApplication.performSelectorOnMainThread(Selector(myFunctionToCall()), withObject: nil, waitUntilDone: true)
此调用编译,我的函数被调用,但在我的后台线程中不在主线程上,并且转储了一个错误:
2015-01-17 20:11:09.142 AudioDatabase[4449:2099588] +[NSApplication (null selector)]: unrecognized selector sent to class 0x7fff7b1d8be0
但执行仍在继续。除了像 NSApplication、NSObject、NSThread 这样的类函数之外,我无法在任何其他类型上调用该函数。但我从来没有用这个到达主循环。
另一个想法是使用 NSInvocation,但是当我查看文档时,只出现了 Objective-C 部分。
如果可能的话,简单地调用我的一个在主线程中运行的带或不带参数的函数会有所帮助。
【问题讨论】:
【参考方案1】:在后台线程中运行我的程序时,我发现了一种在主线程异步执行必要命令的方法。为此,您必须致电:
dispatch_async(dispatch_get_main_queue())
// This block runs in the main thread
所以我的问题是,所以显示和隐藏菜单栏而不会使我的程序崩溃。以下是从后台线程调用时工作的已完成函数:
func m_MenuBarShow ()
dispatch_async(dispatch_get_main_queue())
NSMenu.setMenuBarVisible(true) // Class func, must be called on the Class (NSMenu) and not on the Instance (NSApp.sharedApp.mainMenu)
func m_MenuBarHide ()
dispatch_async(dispatch_get_main_queue())
NSMenu.setMenuBarVisible(false) // Class func
请注意,使用这个有一个小的限制:这个块被称为异步,这意味着你必须确保它已经完成,直到对结果做一些事情。在显示菜单栏的情况下,这没有问题。但是如果你想做一些像打开文件这样的事情,你必须处理这个。
我将对此进行解释,以回答我的另一个问题。请看:Open File Dialog crashes in Swift
【讨论】:
以上是关于在“applicationDidFinishLaunching”方法退出之前,SpriteKit 窗口不会重绘的主要内容,如果未能解决你的问题,请参考以下文章
强制 App 导航到 App DidBecomeActive 中的特定视图
NOIP 2015 & SDOI 2016 Round1 & CTSC 2016 & SDOI2016 Round2游记