我试图解决的最初问题是如何在不阻塞 UI 的情况下截取 UICollectionViewCell 的屏幕截图。我找到了一个解决方案,可以在后台线程上拍摄给定单元格的快照,唯一的问题是如果快照请求在单元格被释放之前没有被执行,它会挂在队列中并最终导致崩溃.
[编辑]我刚刚发现问题与单元格包含 UIWebView 的事实有关。运行以下代码时,我不断收到来自 WebCore 的 EXC_BAD_ACCESS 错误。 当我尝试在没有 UIWebView 的情况下为每个单元格拍摄背景快照时,它工作正常。
谁能确定 UIWebView/WebCore 会导致这个问题和/或我如何解决它?
这是代码:
在我的 UICollectionViewCell 子类中,我创建了一个 NSOperationQueue 的单例实例:
+ (NSOperationQueue*)sharedSnapshotOperation
{
static dispatch_once_t pred;
static NSOperationQueue *shared = nil;
dispatch_once(&pred, ^{
shared = [[NSOperationQueue alloc] init];
shared.name = @"Snapshot Queue";
shared.maxConcurrentOperationCount = 1;
});
return shared;
}
在同一个子类中,我定义了以下用于拍摄快照的方法:
- (void) snapshotAsync:(ImageOutBlock)block
{
CGFloat scaleFactor = [[UIScreen mainScreen] scale];
CGRect frame = self.frame;
__weak CALayer *layer = self.layer;
NSBlockOperation *newBlockOperation = [[NSBlockOperation alloc]init];
[newBlockOperation addExecutionBlock:^{
//TAKE SNAPSHOT OF CELL
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, frame.size.width * scaleFactor, frame.size.height * scaleFactor, 8, frame.size.width * scaleFactor * 4, colorSpace, kCGImageAlphaPremultipliedFirst);
UIGraphicsBeginImageContextWithOptions(frame.size, YES /*opaque*/, scaleFactor);
[layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
//GET MAIN QUEUE FOR DOING ULTIMATE TASK
[[NSOperationQueue mainQueue] addOperationWithBlock:^ {
block(image);
}];
}];
self.blockOperation = newBlockOperation;
//ADD OPERATION TO QUEUE
[[InboxCell sharedSnapshotOperation] addOperation:self.blockOperation];
}
同样,在同一个子类中,我声明了传递给我的异步快照方法的块:
typedef void(^ImageOutBlock)(UIImage* image);
同样,在同一个子类中,我这样调用上述异步快照方法:
__weak typeof(self) weakSelf = self;
[weakSelf snapshotAsync:^(UIImage* image) {
//PUT THE IMAGE IN THE CACHE (ultimate task)
[weakSelf.delegate setCellScreenShot:image forKey:[weakSelf makeIndexingKeyForScreenShot]];
}];
最后,如果单元即将被释放(从 UICollectionView 委托调用),我尝试取消操作请求:
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell: (UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
InboxCell *inboxCell = (InboxCell *)cell;
[inboxCell.blockOperation cancel];
}