SpriteKit窗口不会重绘,直到“applicationDidFinishLaunching”方法退出

时间:2015-01-15 13:59:48

标签: multithreading window menubar

我使用SpriteKit for Mac OS X(不是iOS)来运行我的程序。

" applicationDidFinishLaunching" " AppDelegate"的方法 - 我开始初始化所需的所有事情。有些方法不喜欢从后台线程调用,比如设置窗口标题,调整窗口大小和其他一些任务。所有这些都是在主线程中完成的。

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

在这种情况下,即" applicationDidFinishLaunching"方法不退出,SpriteKit不重绘窗口,所以我的动画运行但我看到一个白色的窗口。

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

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

" applicationDidFinishLaunching"启动后台线程后退出,窗口按预期更新。使用后台线程进行动画时,一切运行良好。

现在问题,我不能解决:我需要隐藏菜单栏,而不是在启动程序时直接隐藏,但是过了一段时间。

NSMenu.setMenuBarVisible(false)

从主线程调用时这样做是没有问题但是如果我从后台线程中隐藏菜单栏,那么我可以隐藏它一次,使其可见一次,隐藏它第二次并使其可见第二次AppDelegate类中的异常停止我的程序:

Thread 1: EXC_BAD_ACCESS (code=EXC_i386_GPFLT)

我解决这个问题的想法是发布一个由主线程处理的事件。但是,如果我发布一个键盘事件,事件处理也是在后台线程中完成的。

用户选择菜单的事件,而不是以编程方式从主线程中获取,但我找不到发布事件的方法,然后在主线程而不是包含sendEvent命令的线程中对事件进行处理:

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 Part。

如果有可能,只需调用我的函数,无论是否有在主线程中运行的参数,都可以做到这一点。

1 个答案:

答案 0 :(得分: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