在后台操作中从NSView绘制没有效果。

4

我正在尝试从后台操作向NSView绘制内容,但没有任何效果。

let queue = OperationQueue()
queue.addOperation() {
  doTheBackgroundStuff()
}

启动后台操作,该操作正在进行大量计算。在AppDelegate中我有

@IBOutlet weak var image: NSImageView!  // some image to show
@IBOutlet weak var number: NSTextField! // a corresponding number
@IBOutlet weak var mainView: NSView!    // the main view holding the above

这个任务

number.intValue = Int32(someNumber)

定期从后台操作发出(经常)。但是文本从不改变。我已经在IB中为该视图以及TextField设置了“可以同时绘制”。我还尝试过

if mainView.lockFocusIfCanDraw() {
  mainView.setNeedsDisplay(mainView.rectPreservedDuringLiveResize)
  mainView.unlockFocus()
}

在文本字段分配之后,也是徒劳无功的。

执行是否到达 if mainView.lockFocusIfCanDraw() { ... } 内部了? - l'L'l
你可能想展示一下你正在使用的更新文本框的代码。 - l'L'l
“can draw concurrently” 是一个虚假选项,它表示 已经专门实现了 -drawRect: 来支持并发绘制。它与 AppKit 对视图的处理方式无关。除非你真的需要在此处进行锁定,请不要设置此选项。 - CodaFi
@CodaFi 非常感谢您的指引,之前我只是抓住了救命稻草才设定了这个设置。现在我面临着更加未知的问题_initWithWindowNumber: error creating graphics ctxt object for ctxt。顺便提一下,这个问题与flushGraphics有关,但即使禁用了它,仍然没有改变。 - qwerty_so
尝试使用 self.performSelector(onMainThread: Selector, with: Any?, waitUntilDone: Bool) 方法在主线程上设置文本字段的文本。 - Rohit Parsana
显示剩余5条评论
2个回答

3

按照https://dev59.com/lnnZa4cB1Zd3GeqPlxYf#19997698中所述,调用flushGraphics

我在Thread guide中了解到关于NSGraphicsContext限制的信息。

在这里,我发现了以下内容:

如果您从辅助线程绘制任何图形,则必须手动刷新绘制调用。 Cocoa不会自动使用从辅助线程绘制的内容更新视图,因此当您完成绘制时,需要调用NSGraphicsContext的flushGraphics方法如果您的应用仅从主线程绘制内容,则不需要刷新绘制调用。


我在锁定部分尝试了 NSGraphicsContext(window:window!).flushGraphics(),但没有任何变化。 - qwerty_so
这将为您获取一个新的图形上下文,您需要与您的窗口相关联的那个。同样,请确保从主线程中检索此上下文,因为图形上下文是线程本地的。 - CodaFi
当我将这段代码添加到我的程序中时,出现了“_initWithWindowNumber: error creating graphics ctxt object for ctx”错误。 - qwerty_so

2
通常情况下,如果您将视图更新代码从后台任务发送回主队列,则这些问题会消失:
DispatchQueue.main.async {
    // your view update code
}

如果你的doTheBackgroundStuff中没有太多地方,你可以随时添加一些用于视图更新的代码,即每当你访问mainView时。
否则,将事物重新分组为执行非UI繁重工作的部分,然后在最后将视图更新推送到Dispatch.main.async会有所帮助。

那差不多可以了。我能看到字段正在改变。但是速度慢得像糖蜜一样。可能整个窗口设计并不适合我需要的大规模更新。 - qwerty_so
我建议将更新合并成在合理的时间间隔内刷新的组。如果您的更新适合这样做,甚至可以平稳地过渡。如果您让UIView在状态之间执行动画,您将获得比尝试过于频繁地推送更新更平滑的结果。 - sas
是的,这样可能会使它更流畅。但这需要我找到一种智能缓冲更新的方法(比如每秒25次)。也许一个简单的计时器就可以完成这个工作。 - qwerty_so
您可以使用调度源将更改排队等待一段时间,只有最后一个更改实际执行。例如,我之前写了一个Throttle结构体,用于缓冲/节流网络请求,您可以采用它。请参见测试以了解如何使用它。如果它能正常工作,那么很高兴获得奖励 :) - sas
虽然它并没有完全解决我的问题(这些问题很可能是系统/操作系统固有的),但我会将其接受为正确答案。让你超过1千分数;-) - qwerty_so

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接