原帖:http://objccn.io/issue-3-1/
我们除了在主队列中可以向 CALayer 的后备存储中绘制一些东西,其他方法都将不可行。可怕的事情将会发生。我们能做的就是向一个完全断开链接的位图上下文中进行绘制。
正如我们上面所提到的一样,在 Core Graphics 下,所有 Core Graphics 绘制方法都需要一个上下文参数来指定绘制到那个上下文中。UIKit 有一个当前上下文的概念(也就是绘制到哪儿去)。这个当前的上下文就是 per-thread.
为了同时绘制,我们需要做下面的操作。我们需要在另一个队列创建一个图像,一旦我们拥有了图像,我们可以切换回主队列,并且设置这个图像为 UIImageView 的图像
增加一个你可以在其中绘制的新方法:
- (UIImage *)renderInImageOfSize:(CGSize)size
{
UIGraphicsBeginImageContextWithOptions(size, NO, 0);
// do drawing here
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
这个方法通过 UIGraphicsBeginImageContextWithOptions()
方法,并根据给定的大小创建一个新的 CGContextRef 位图。这个方法也会将这个上下文设置为当前UIKit的上下文。现在你可以在这里做你想在 -drawRect:
中做的事了。然后我们可以通过UIGraphicsGetImageFromCurrentImageContext()
,将获得的这个上下文位图数据作为一个
UIImage,最终移除这个上下文。
很重要的一点就是,你在这个方法中所做的所有绘图的代码都是线程安全的,也就是说,当你访问属性等等,他们需要线程安全。因为你是在另一个队列中调用这个方法的。如果这个方法在你的视图类中,那就需要注意一点了。另一个选择就是创建一个单独的渲染类,并设置所有需要的属性,然后通过触发来渲染图片。如果这样,你可以通过使用简单的 UIImageView 或者 UITableViewCell。
要知道,所有 UIKit 的绘制 API 在使用另一个队列时,都是安全的。只需要确定是在同一个操作中调用他们的,这个操作需要以UIGraphicsBeginImageContextWithOptions()
开始,以 UIGraphicsEndIamgeContext()
结束。
你需要像下面这样触发渲染代码:
UIImageView *view; // assume we have this
NSOperationQueue *renderQueue; // assume we have this
CGSize size = view.bounds.size;
[renderQueue addOperationWithBlock:^(){
UIImage *image = [renderer renderInImageOfSize:size];
[[NSOperationQueue mainQueue] addOperationWithBlock:^(){
view.image = image;
}];
}];