使用Photos框架的视频选择器

时间:2016-11-02 15:31:07

标签: ios swift photosframework

我正在尝试使用Photos框架提取用户库中的所有视频,并将其显示在集合视图中。基本上是我自己的视频选择器。

我正在使用PHCachingImageManager开始缓存每个视频的图像缩略图。当'cellForItemAt indexPath'被调用时,当我调用PHCachingImageManager.requestImage(...)并尝试在闭包中设置我的单元格图像时,我得到'在展开Optional值时意外发现nil'。

viewDidLoad() {
    // Register Cell classes
    self.collectionView!.register(PhotosCell.self, forCellWithReuseIdentifier: reuseIdentifier)

    let videosAlbum = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .smartAlbumVideos, options: nil)

    print(videosAlbum.count) // prints as 1 bc videosAlbum contains 1 collection
    self.videos = PHAsset.fetchAssets(in: videosAlbum.object(at: 0), options: nil)
    print(self.videos.count) // prints the # of videos in the Videos smart album

    self.imageManager.startCachingImages(for: self.videos.objects(at: [0, videos.count-1]), targetSize: CGSize(width: 150, height: 150), contentMode: .aspectFit, options: nil)
}

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! PhotosCell
    let asset = videos.object(at: indexPath.item)

    imageManager.requestImage(for: asset, targetSize: CGSize(width: 150, height: 150), contentMode: .aspectFill, options: nil, resultHandler: {
        image, _ in
                cell.thumbnailImage = image //this is the line throwing the error
    })
    return cell
}

这可能是我实现PhotosCell类的方式有问题吗?或者我如何使用'collectionView.dequeueReusableCell(...)?

的问题
class PhotosCell: UICollectionViewCell {

@IBOutlet weak var photoThumbnail: UIImageView!

var thumbnailImage: UIImage! {
    didSet {
        photoThumbnail.image = thumbnailImage
    }
}
}

我环顾四周,以为我看到传递给缓存管理器的闭包放在后台线程中,但这不应该影响在错误行上设置的可选UIImageView.image,对吗? 对于更多上下文,GitHub代码为here

2 个答案:

答案 0 :(得分:0)

出于某种原因,删除这些行:

// Register Cell classes
self.collectionView!.register(PhotosCell.self, forCellWithReuseIdentifier: reuseIdentifier)

在ViewDidLoad()方法中,它可以很好地显示缩略图。之前,单元格没有被初始化(我假设),并且在尝试设置单元格的UIImageView.image时抛出错误。

不确定这是否会影响UICollectionView的任何其他功能......

答案 1 :(得分:0)

由于你的自我答案在你不确定原因的情况下解决了问题,这里有更全面的解释......

在故事板中设置原型单元格时,它会自动将该单元格定义注册到集合视图,因此您无需致电registerCellClassregisterNib

事实上,如果您 致电registerCellClassdequeueReusableCell将尝试使用您的手机类创建新的手机实例'默认初始值设定项(init(frame:)最有可能)。这意味着它不会从故事板中加载您的单元格类,因此您在那里建立的任何IBOutlet个连接都无法连接 - 因此您的单元格photoThumbnail出口是没有,导致你找到的陷阱。

所以你的自我答案是正确的 - 只是不要打电话给registerCellClass并让故事板为你处理。 (在幕后,Xcode将您的故事板编译成多个笔尖,并且笔尖加载代码使用您设置为原型单元格的那个调用registerNib:forCellWithReuseIdentifier:。)

虽然我们在这里,但请修复代码中的另一个问题。在requestImage返回之后,您的collectionView(_:cellForItemAt:)完成处理程序才会执行。这意味着在滚动期间或之后,您正在重新设置图像的单元格可能已被重复使用 - 因此它现在表示与您请求图像的索引不同的索引路径,并且您现在正在设置错误图像就可以了。

如果您查看Apple的Using Photos Framework示例代码,他们会做一些额外的工作,以确保单元格上图像的异步设置正常。在他们的AssetGridViewController

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let asset = fetchResult.object(at: indexPath.item)

    // Dequeue a GridViewCell.
    guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: String(describing: GridViewCell.self), for: indexPath) as? GridViewCell
        else { fatalError("unexpected cell in collection view") }

    // Request an image for the asset from the PHCachingImageManager.
    cell.representedAssetIdentifier = asset.localIdentifier
    imageManager.requestImage(for: asset, targetSize: thumbnailSize, contentMode: .aspectFill, options: nil, resultHandler: { image, _ in
        // The cell may have been recycled by the time this handler gets called;
        // set the cell's thumbnail image only if it's still showing the same asset.
        if cell.representedAssetIdentifier == asset.localIdentifier {
            cell.thumbnailImage = image
        }
    })
    return cell
}