NSView调用display立即触发drawRect,子视图未更新,如何在NSApp循环外刷新?
首先明确:Cocoa的UI绘制必须在主线程执行,且依赖NSRunLoop处理绘制事件。如果你的长时间任务在主线程运行,哪怕调用display,主线程也会被任务占用,根本没空处理绘制操作,所以按钮不会立即刷新。
给你几个可行方案:
方案1:把长时间任务移到后台线程(推荐)
这是最规范的做法,主线程只负责UI操作,耗时任务丢到后台:
// 切换到后台线程执行耗时任务 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 执行你的长时间处理任务 [self doLongRunningTask]; // 任务完成后切回主线程更新UI dispatch_async(dispatch_get_main_queue(), ^{ [myButton setAttributedTitle:colorTitle]; // 这里无需手动调用setNeedsDisplay,系统会自动处理 }); });
如果需要在任务进行中更新UI(比如中途改变按钮颜色),同样在后台任务里切回主线程执行UI更新代码即可,主线程不被阻塞,UI会立即刷新。
方案2:手动触发主线程RunLoop处理事件(应急方案)
如果没法把任务移到后台(比如遗留代码限制),可以在长时间任务执行过程中,每隔一段时间手动让RunLoop处理一次pending的绘制事件:
// 设置按钮的富文本标题 [myButton setAttributedTitle:colorTitle]; // 标记按钮需要重绘 [myButton setNeedsDisplay:YES]; // 手动触发一次RunLoop,处理绘制事件 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0]]; // 继续执行长时间任务 [self doLongRunningTask];
注意:这种方式会打断长时间任务的执行来处理UI事件,不要过于频繁调用,避免拖慢任务速度。
方案3:直接调用按钮的displayIfNeeded
比起调用父视图的setNeedsDisplay,直接针对按钮操作更高效:
[myButton setAttributedTitle:colorTitle]; [myButton displayIfNeeded]; // 配合手动RunLoop触发,才能在主线程被阻塞时生效 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0]];
总结:优先用方案1,后台线程执行耗时任务、主线程更新UI是Cocoa开发的最佳实践,能从根源避免UI阻塞问题。
内容的提问来源于stack exchange,提问作者ImageLite
相关产品推荐
相关产品推荐

