如果我正确理解了您的问题,则有一个简单的解决方案。在您长时间运行的例程中,您需要告诉当前的 runloop 在您自己处理的某些点处处理一次迭代(或更多的 runloop)。例如,当您想要更新显示时。任何具有脏更新区域的视图都会在运行 runloop 时调用其 drawRect: 方法。
告诉当前运行循环处理一次迭代(然后返回给你......):
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate date]];
这是一个(低效)长时间运行例程的示例,带有相应的 drawRect - 每个都在自定义 UIView 的上下文中:
- (void) longRunningRoutine:(id)sender
{
srand( time( NULL ) );
CGFloat x = 0;
CGFloat y = 0;
[_path moveToPoint: CGPointMake(0, 0)];
for ( int j = 0 ; j < 1000 ; j++ )
{
x = 0;
y = (CGFloat)(rand() % (int)self.bounds.size.height);
[_path addLineToPoint: CGPointMake( x, y)];
y = 0;
x = (CGFloat)(rand() % (int)self.bounds.size.width);
[_path addLineToPoint: CGPointMake( x, y)];
x = self.bounds.size.width;
y = (CGFloat)(rand() % (int)self.bounds.size.height);
[_path addLineToPoint: CGPointMake( x, y)];
y = self.bounds.size.height;
x = (CGFloat)(rand() % (int)self.bounds.size.width);
[_path addLineToPoint: CGPointMake( x, y)];
[self setNeedsDisplay];
[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate date]];
}
[_path removeAllPoints];
}
- (void) drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor( ctx, [UIColor blueColor].CGColor );
CGContextFillRect( ctx, rect);
CGContextSetStrokeColorWithColor( ctx, [UIColor whiteColor].CGColor );
[_path stroke];
}
这是一个展示这种技术的完整工作示例。
通过一些调整,您可能可以调整它以使 UI 的其余部分(即用户输入)也响应。
更新(使用此技术的警告)
我只想说我同意这里其他人的大部分反馈,说这个解决方案(调用 runMode: 强制调用 drawRect:) 不一定是个好主意。我已经回答了这个问题,我认为这是对所述问题的事实“这是如何”的答案,我不打算将其宣传为“正确的”架构。另外,我并不是说可能没有其他(更好的?)方法可以达到相同的效果——当然可能还有其他我不知道的方法。
更新(回应乔的示例代码和性能问题)
您看到的性能下降是在绘图代码的每次迭代中运行 runloop 的开销,其中包括将图层渲染到屏幕以及 runloop 所做的所有其他处理,例如输入收集和处理。
一种选择可能是不那么频繁地调用 runloop。
另一种选择可能是优化您的绘图代码。就目前而言(我不知道这是您的实际应用程序,还是只是您的示例......)您可以做一些事情来让它更快。我要做的第一件事是将所有 UIGraphicsGet/Save/Restore 代码移到循环之外。
然而,从架构的角度来看,我强烈建议您考虑这里提到的其他一些方法。我看不出为什么你不能在后台线程上构造你的绘图(算法不变),并使用计时器或其他机制来通知主线程以某种频率更新它的 UI,直到绘图完成。我认为参与讨论的大多数人都会同意这将是“正确”的方法。