如何强制刷新 NSTextField 标签

How To Force A NSTextField Label To Refresh

我想做的事情看起来很简单,但是实现却让我很吃力。如何在更改标签值后强制刷新标签?

我刚刚创建了一个简单的进度 window。当主应用程序正在做某事时,我弹出了进度 window,我想在循环执行时更新标签和进度条的值。

我创建了一个新的 NSWindow 和 XIB 文件,其中包含标签和进度条的出口,我正在加载它:

_pbWindow                           = [[ProgressBar alloc] initWithWindowNibName:@"ProgressBar"];

[_pbWindow showWindow:self];

当我打电话时:

_pbWindow.lblProgress1.stringValue = @"Doing this now...";

我无法让标签在新 window 中实际刷新。

我在这里和 google 进行了搜索,但似乎找不到任何可以告诉我如何在不同的 window 中执行此操作的信息,只有当它位于主要 window 它只会刷新(无论如何,这一直是我的经验)。

我想可能是因为 window 没有焦点,所以我尝试以模态方式打开它:

[[NSApplication sharedApplication] beginSheet:[_pbWindow window]
                               modalForWindow:[[NSApplication sharedApplication] mainWindow]
                                modalDelegate:_pbWindow
                               didEndSelector:nil
                                  contextInfo:nil];

还是不行。

当您设置文本字段的 stringValue 时,文本字段会将自己标记为需要显示(-setNeedsDisplay... 方法之一)。它不会立即显示自己。稍后,在主事件循环期间,应用程序将把它的每个 windows 告诉 -displayIfNeeded,文本字段的新内容将实际绘制在屏幕上。

但是,由于您处于 运行 长循环中,因此您不允许主事件循环进入 运行 并且 windows 不会得到如果需要,有机会展示。

一种解决方法是在更改 stringValue:

后直接在文本字段上调用 ​​-displayIfNeeded
_pbWindow.lblProgress1.stringValue = @"Doing this now...";
[_pbWindow.lblProgress1 displayIfNeeded];

但是,不让主事件循环还有其他问题运行。只是解决这个问题仍然存在其他问题。例如,您的应用程序将在 Activity 监视器中显示为无响应,并且当鼠标悬停在其 windows 上时,系统将显示旋转的色轮光标。您应该考虑寻找另一种方式来构建您的应用程序。

例如,您可以 运行 您的进度 window 作为模态 window 在主线程上(使用 -[NSApplication runModalForWindow:])并在后台线程上完成工作.工作完成后,调用 -[NSApplication stopModal...] 方法或 -[NSApplication abortModal] 之一(但请阅读文档以了解在什么情况下应该使用哪个,尽管在 10.9 中取消了对 -stopModal... 的一些限制) .主线程将被允许返回到主事件循环,但是模式 window 会阻止用户对 UI 的其余部分做任何他们不应该做的事情 while手术继续进行。

或者,也许该操作实际上不应该垄断您的整个应用程序。也许它应该只是单个 window 的模态。在这种情况下,您可以在 window 上将进度 window 显示为 sheet,在操作期间应将其禁用。使用 -[NSWindow beginSheet:completionHandler:] 开始 sheet 并在后台启动操作。操作完成后,在主线程上调用 -[NSWindow endSheet:...] 方法之一。

如果您 运行 在后台线程上工作,请始终确保使用 GCD 和 dispatch_get_main_queue() 或 [将影响 GUI 的任何更改分流到主线程=23=].