如何使用Photo Kit获取相册海报图像?

12

当使用Assets Library时,您可以从ALAssetsGroup中获取相册的海报图像。 当使用Photos Framework(Photo kit)时,如何实现相同效果?


1
你尝试过什么?发一下代码,这样其他用户就可以检查了。 - Ashish Kakkad
请查看苹果的示例代码:https://developer.apple.com/documentation/photokit/browsing_and_modifying_photo_albums - iAleksandr
3个回答

16

我这样做。在方法cellForRowAtIndexPath:中,在albums表格视图中添加以下代码。

Objective C:

    PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
    fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
    PHFetchResult *fetchResult = [PHAsset fetchKeyAssetsInAssetCollection:[self.assetCollections objectAtIndex:indexPath.row] options:fetchOptions];
        PHAsset *asset = [fetchResult firstObject];
        PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
        options.resizeMode = PHImageRequestOptionsResizeModeExact;

        CGFloat scale = [UIScreen mainScreen].scale;
        CGFloat dimension = 78.0f;
        CGSize size = CGSizeMake(dimension*scale, dimension*scale);

        [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:size contentMode:PHImageContentModeAspectFill options:options resultHandler:^(UIImage *result, NSDictionary *info) {
        dispatch_async(dispatch_get_main_queue(), ^{
            cell.imageView.image = result;
        });

        }];

Swift 3:

let fetchOptions = PHFetchOptions()
let descriptor = NSSortDescriptor(key: "creationDate", ascending: true)
fetchOptions.sortDescriptors = [descriptor]

let fetchResult = PHAsset.fetchKeyAssets(in: assets[indexPath.row], options: fetchOptions)

guard let asset = fetchResult?.firstObject else {
    return
}

let options = PHImageRequestOptions()
options.resizeMode = .exact

let scale = UIScreen.main.scale
let dimension = CGFloat(78.0)
let size = CGSize(width: dimension * scale, height: dimension * scale)


PHImageManager.default().requestImage(for: asset, targetSize: size, contentMode: .aspectFill, options: options) { (image, info) in
    DispatchQueue.main.async {
        cell.imageView.image = image
    }
}

编辑: 看起来fetchKeyAssetsInAssetCollection并不总是返回正确的结果(最近捕获的图像/视频)。keyAssets的定义模糊不清,由苹果官方文档提供。建议改用

+ (PHFetchResult *)fetchAssetsInAssetCollection:(PHAssetCollection *)assetCollection options:(PHFetchOptions *)options

获取获取结果数组,然后从获取结果中获取第一个对象,就像之前描述的那样。这肯定会返回正确的结果。 :)


你能具体说明一下“不总是返回正确的结果”吗? - Bill
cell.imageView.image = image 需要在主线程中执行。不能从非主线程修改UI元素。 - Bill
感谢您指出主线程问题,已经编辑了代码。 另外,关于您对“正确结果”的问题,基本上fetchKeyAssetsInAssetCollection不会返回照片库中最顶部的图片。API文档非常含糊。另一个API fetchAssetsInAssetCollection则可以确定地获取相册中最顶部的图片。希望这解决了您的疑问。 - jarora
我无法让它工作。因为cellForRowAtIndexPath:返回单元格之前,requestImageForAsset完成块还没有返回图像,所以单元格的imageView框架是{0,0,0,0}。这意味着您无法看到图像。如果在完成块中手动设置imageView的框架,则不会将标签移出路径。我目前正在检索重新加载tableview之前的所有缩略图。我对PHPhotos并不印象深刻-在我看来,ALAssets要容易得多。 - mashers
@mashers 为什么不使用一个虚拟图像,当你检索到原始图像时再替换它呢?这样你就可以得到正确大小的imageView(我相信你的imageView是固定大小的)。 Photos框架有一些问题。我已经提出了几个关于缺少功能的radar。 - jarora
一个虚拟图像是个好主意。我没想到过。不过我用另一种方式让它工作了,通过循环获取所有缩略图,然后再触发tableView重新加载数据。你的方法听起来更简洁。 - mashers

5

这是我现在使用的内容。它可以处理不同类型的照片集合以及当关键资产丢失时的情况。

static func fetchThumbnail(collection: PHCollection, targetSize: CGSize, completion: @escaping (UIImage?) -> ()) {

    func fetchAsset(asset: PHAsset, targetSize: CGSize, completion: @escaping (UIImage?) -> ()) {
        let options = PHImageRequestOptions()
        options.deliveryMode = PHImageRequestOptionsDeliveryMode.highQualityFormat
        options.isSynchronous = false
        options.isNetworkAccessAllowed = true

        // We could use PHCachingImageManager for better performance here
        PHImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: .default, options: options, resultHandler: { (image, info) in
            completion(image)
        })
    }

    func fetchFirstImageThumbnail(collection: PHAssetCollection, targetSize: CGSize, completion: @escaping (UIImage?) -> ()) {
        // We could sort by creation date here if we want
        let assets = PHAsset.fetchAssets(in: collection, options: PHFetchOptions())
        if let asset = assets.firstObject {
            fetchAsset(asset: asset, targetSize: targetSize, completion: completion)
        } else {
            completion(nil)
        }
    }

    if let collection = collection as? PHAssetCollection {
        let assets = PHAsset.fetchKeyAssets(in: collection, options: PHFetchOptions())

        if let keyAsset = assets?.firstObject {
            fetchAsset(asset: keyAsset, targetSize: targetSize) { (image) in
                if let image = image {
                    completion(image)
                } else {
                    fetchFirstImageThumbnail(collection: collection, targetSize: targetSize, completion: completion)
                }
            }
        } else {
            fetchFirstImageThumbnail(collection: collection, targetSize: targetSize, completion: completion)
        }
    } else if let collection = collection as? PHCollectionList {
        // For folders we get the first available thumbnail from sub-folders/albums
        // possible improvement - make a "tile" thumbnail with 4 images
        let inner = PHCollection.fetchCollections(in: collection, options: PHFetchOptions())
        inner.enumerateObjects { (innerCollection, idx, stop) in
            self.fetchThumbnail(collection: innerCollection, targetSize: targetSize, completion: { (image) in
                if image != nil {
                    completion(image)
                    stop.pointee = true
                } else if idx >= inner.count - 1 {
                    completion(nil)
                }
            })
        }
    } else {
        // We shouldn't get here
        completion(nil)
    }
}

从PHCollection中获取缩略图的工作完美。 - Jaydeep Patel

1

这是一个非常简单的事情...

    PHFetchOptions *userAlbumsOptions = [PHFetchOptions new];
    userAlbumsOptions.predicate = [NSPredicate predicateWithFormat:@"estimatedAssetCount > 0"];

    PHFetchResult *userAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAny options:userAlbumsOptions];

    [userAlbums enumerateObjectsUsingBlock:^(PHAssetCollection *collection, NSUInteger idx, BOOL *stop) {
    NSLog(@"album title %@", collection.localizedTitle);
    PHFetchResult *assetsFetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:nil];
    PHAsset *asset = [assetsFetchResult objectAtIndex:0];
    NSInteger retinaMultiplier = [UIScreen mainScreen].scale;
    CGSize retinaSquare = CGSizeMake(80 * retinaMultiplier, 80 * retinaMultiplier);

    [[SDWebImageManager sharedManager] downloadImageWithURL:[NSURL URLWithString:asset.localIdentifier] options:SDWebImageProgressiveDownload targetLocalAssetSize:retinaSquare progress:^(NSInteger receivedSize, NSInteger expectedSize) {

    } completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
        if (image) {
            albumCoverImg.image = image;
        }
    }];
}];

如果您没有更新SDWebImage类,则可以像正常方式一样加载图像。

1
为什么这个答案引入了第三方依赖(SDWebImage)? - Bill
3
我不确定这是否正确。它应该使用“fetchKeyAssets”,而不仅仅是获取第一个对象(关键资产不一定只是返回的第一个资产)。 - Bill

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接