2

注意:以下更新...

我有一个可可桌面应用程序,它由一系列围绕自定义 NSView 的控件组成。我正在使用 displayLink 来驱动更新。

当用户单击 NSControl(滑块、按钮、复选框、单选按钮)时,应用程序似乎会冻结,直到释放鼠标。事实上,我可以确认 displayLink 回调(getFrameForTime)在此期间没有触发。如果我创建一个也不会触发的计时器,它们都会保持暂停状态,直到用户释放鼠标,此时应用程序会继续更新。

控件已绑定,如果我从另一个线程更新该值(例如,通过来自 MIDI 接口的回调),滑块的行为与预期一样:它移动,值更新并且应用程序不会暂停。

我觉得这应该是一个相当明显的修复,但我很难过。

  • 在 IB 中检查“连续”就像宣传的那样:连续发送值,但在释放鼠标之前仍然表现出这种行为(阻止 UI 更新)。

  • 这似乎与 NSControl 上的 mouseDown 相关?为什么会阻塞,我真的需要子类化我所有的 UI 元素来改变这种行为(似乎很极端)

  • DisplayLink 是在自己的线程中,那为什么主线程上的 mouseDown 会阻塞呢?如果是这种情况,考虑到从主线程以外的地方更新 Cocoa UI 的禁令,我该如何处理呢?

非常感谢任何帮助。

更新

根据@Nikolai 下面的评论,我可以确认使用 NSTimer 并将其添加到 NSEventTrackingRunLoopMode 不会阻塞。但是,我真的很想使用 CVDisplayLink (根据文档)在它自己的线程中运行并且不应该以这种方式被阻止。与 CADisplayLink 不同,我找不到将运行循环显式分配给 CVDisplayLink 的方法(似乎它不是那样工作的),所以也许新问题应该是:

为什么 CVDisplayLink 会阻塞 NSEventTrackingRunLoopMode?

4

2 回答 2

3

当单击 NSControl 时,只要鼠标按下, runloop 模式就会从NSDefaultRunLoopMode变为。NSEventTrackingRunLoopMode这意味着只有添加到此模式的运行循环源(显示链接)和计时器才会触发。

您可以使用 将计时器添加到任何模式-[NSRunLoop addTimer:forMode:]。对于显示链接,等效方法是-[CADisplayLink addToRunLoop:forMode:]

为了让您的动画在事件跟踪期间继续,您可以执行以下操作:

[myDisplayLink addToRunLoop:[NSRunLoop currentRunLoop]
                    forMode:NSEventTrackingRunLoopMode];
于 2013-08-29T12:10:24.817 回答
2

您的测试项目表明您正在display从显示链接的回调中调用视图的方法。

注释掉display消息时,即使在移动滑块时也会连续调用显示链接。

所以问题是当runloop进入事件跟踪模式时,display显示链接线程上的调用会阻塞,直到鼠标被释放并且runloop回到默认模式。您可以通过在调用之前和之后放置一个日志语句来轻松地确认这一点。

我不清楚为什么会发生这种情况。很明显,从后台线程调用视图的方法是非法的。setNeedsDisplay:您必须通过在主线程上调度 a 来触发视图的显示:

static CVReturn MyDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* now, const CVTimeStamp* outputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [(__bridge MyCustomView*)displayLinkContext setNeedsDisplay:YES];
    });
    return kCVReturnSuccess;
}
于 2013-08-29T13:42:14.503 回答