2

我试图解决的最初问题是如何在不阻塞 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];
}
4

1 回答 1

0

您是否尝试过创建自己的自定义 NSOperation?

这个想法是在您的单元格中保留一个指向 NSOperation 的强指针,该指针将对其进行截图。当您的单元格将被重用(方法 prepareForReuse)或解除分配时,请验证 NSOperation 的状态(isFinished 属性),如果它不只是取消它。

我建议在 UICollectionView 委托中创建自己的队列并在其他线程中处理操作。

查看 NSOperation API 文档: https ://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html

于 2013-09-11T22:05:53.947 回答