SpriteKit windows 在 "applicationDidFinishLaunching" 方法退出之前不重绘

SpriteKit windows do not redraw until "applicationDidFinishLaunching" method quits

我将 SpriteKit 用于 Mac OS X(不是 iOS)到 运行 我的程序。

在 "AppDelegate"-Class 的 "applicationDidFinishLaunching" 方法的末尾,我开始了初始化所需的所有事情。有些方法不喜欢从 background-thread 中调用,例如设置 window-titles、调整大小 windows 和其他一些任务。所以这些事情都是在main-thread.

中完成的

然后我们来解决我的问题:我不能在 "applicationDidFinishLaunching" 方法的末尾简单地 运行 我的主程序,因为当我这样做时, "applicationDidFinishLaunching" 方法不会退出直到我的主程序退出。而我的主程序并没有退出,因为它在启动程序后直接在屏幕上显示了一些动画。

如果 "applicationDidFinishLaunching" 方法没有退出,SpriteKit 不会重绘 window,所以我的动画 运行s 但我看到白色 window.

退出我的程序后,"applicationDidFinishLaunching"方法也退出了,我看到了动画的最后一张图片。

所以我想到了一个解决方法:我现在在 "applicationDidFinishLaunching" 方法中进行初始化,然后启动一个后台线程,该线程 运行 是我的主程序。

"applicationDidFinishLaunching" 在启动 background-thread 后退出,window 按预期更新。 运行一切正常,background-thread 正在制作动画。

现在的问题,我没有解决:我需要隐藏菜单栏,不是在启动程序时直接隐藏,而是在一段时间后隐藏。

NSMenu.setMenuBarVisible(false)

从 main-thread 调用时这样做没有问题,但如果我从后台线程中隐藏 menu-bar,那么我可以将其隐藏一次,使其可见一次,再隐藏一次时间和第二次使其可见时,AppDelegate 中的异常 Class 停止了我的程序:

Thread 1: EXC_BAD_ACCESS (code=EXC_i386_GPFLT)

我解决这个问题的想法是 post 一个由 main-thread 处理的事件。但是,如果我 post 一个键盘事件,event-handling 也在 background-thread 内完成。

像用户选择菜单这样的事件,不是以编程方式从主线程处理的,但我没有找到一种方法来 post 一个事件,然后在主线程而不是线程中处理,其中包含 sendEvent-command:

NSApplication.sharedApplication().sendEvent(event!) // Called from background-thread

有没有人想过发送一个由 main-thread

处理的事件

运行我的程序完全在main-thread没有问题,就是window-content根本不画。第二种解决方案是我最喜欢的,因为还有一些东西会在后台线程中产生问题。

也许我可以从另一种方法启动我的主程序,在 "applicationDidFinishLaunching" 完成后的某个时间。


关于上述主题的一些更深入的信息,但仍然没有解决方案:

我发现,存在一个可以从 swift 调用的函数 "performSelectorOnMainThread",如下所示:

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 等少数类型之外,我无法像 class 函数那样调用该函数。但是我从来没有用这个到达主循环。

另一个想法是使用 NSInvocation,但是当我查看文档时,只出现了 Objective-C 部分。

如果可能的话,在主线程中使用或不使用 运行 的参数简单地调用我的函数并且可以在那里做一些事情会有所帮助。

虽然 运行 我的程序在后台线程中,但我发现了一种方法,可以在主线程中异步执行必要的命令。为此,您必须调用:

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