问题 检查给定的PHAsset是iCloud资产吗?


我正在尝试获取PhAsset对象。我想隔离iCloud资产。这是我的代码,

PHFetchResult *cloudAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAny options:nil];

[cloudAlbums enumerateObjectsUsingBlock:^(PHAssetCollection *collection, NSUInteger idx, BOOL *stop){
    if(collection != nil){

        PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:collection options:fetchOptions];
        [result enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop)
         {
            // check asset is iCloud asset 

         }];
    }

}];

请告诉我如何找到PHAsset是iCloud资产?


10563
2017-08-12 13:25


起源



答案:


实际上有两种情况: 1.照片由此设备捕获,并上传到iCloud。然后,您可以使用progressHandler检查是否需要iCloud下载。

__block BOOL isPhotoInICloud = NO;
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.networkAccessAllowed = YES;
options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info){

        isPhotoInICloud = YES;

});

[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {

        if (isPhotoInICloud) {
            // Photo is in iCloud.
        }
});
  1. 照片在iCloud中,但从其他设备上传。而且您没有将其保存到您当地的照片库。因此,永远不会调用progressHandler块。我不知道为什么,但这是真的,我认为这是PhotoKit框架的一个错误。 对于这种情况,如果使用PHImageResultIsInCloudKey,那也很困难。因为您可以在requestImageForAsset的resultHandler块中知道PHImageResultIsInCloudKey值。但这是照片请求启动后的时间。

所以,至少在我看来,没有办法检查照片是否存储在iCloud中。 也许还有其他更好的方法,请告诉我。 非常感谢!


6
2017-11-01 03:22





这有点像黑客,我不得不挖掘资源数组并调试以找出我需要的信息。但它的确有效。虽然这是一个未记录的代码,但我不确定苹果是否会因为这个而拒绝该应用程序。试一试看看会发生什么!

// asset is a PHAsset object for which you want to get the information    
NSArray *resourceArray = [PHAssetResource assetResourcesForAsset:asset];
BOOL bIsLocallayAvailable = [[resourceArray.firstObject valueForKey:@"locallyAvailable"] boolValue]; // If this returns NO, then the asset is in iCloud and not saved locally yet

您还可以从资产资源中获取一些其他有用的信息,例如 - 原始文件名,文件大小,文件URL等。


3
2017-09-24 12:17



我测试一下,它适用于视频。 - Red Mak


答案:


实际上有两种情况: 1.照片由此设备捕获,并上传到iCloud。然后,您可以使用progressHandler检查是否需要iCloud下载。

__block BOOL isPhotoInICloud = NO;
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.networkAccessAllowed = YES;
options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info){

        isPhotoInICloud = YES;

});

[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFit options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {

        if (isPhotoInICloud) {
            // Photo is in iCloud.
        }
});
  1. 照片在iCloud中,但从其他设备上传。而且您没有将其保存到您当地的照片库。因此,永远不会调用progressHandler块。我不知道为什么,但这是真的,我认为这是PhotoKit框架的一个错误。 对于这种情况,如果使用PHImageResultIsInCloudKey,那也很困难。因为您可以在requestImageForAsset的resultHandler块中知道PHImageResultIsInCloudKey值。但这是照片请求启动后的时间。

所以,至少在我看来,没有办法检查照片是否存储在iCloud中。 也许还有其他更好的方法,请告诉我。 非常感谢!


6
2017-11-01 03:22





这有点像黑客,我不得不挖掘资源数组并调试以找出我需要的信息。但它的确有效。虽然这是一个未记录的代码,但我不确定苹果是否会因为这个而拒绝该应用程序。试一试看看会发生什么!

// asset is a PHAsset object for which you want to get the information    
NSArray *resourceArray = [PHAssetResource assetResourcesForAsset:asset];
BOOL bIsLocallayAvailable = [[resourceArray.firstObject valueForKey:@"locallyAvailable"] boolValue]; // If this returns NO, then the asset is in iCloud and not saved locally yet

您还可以从资产资源中获取一些其他有用的信息,例如 - 原始文件名,文件大小,文件URL等。


3
2017-09-24 12:17



我测试一下,它适用于视频。 - Red Mak


当您请求图像时,您会在信息词典中获得一个密钥,该密钥会告诉您资产是否存在于iCloud中。

[cloudAlbums enumerateObjectsUsingBlock:^(PHAssetCollection *collection, NSUInteger idx, BOOL *stop)
{
    PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:collection options:fetchOptions];
    [result enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop)
    {  
        PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
        options.resizeMode = PHImageRequestOptionsResizeModeFast;
        options.synchronous = YES;
        __block BOOL isICloudAsset = NO;
        [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:imageSize contentMode:PHImageContentModeAspectFit options:options resultHandler:^(UIImage *result, NSDictionary *info) 
        {
            if ([info objectForKey: PHImageResultIsInCloudKey].boolValue) 
            {
                isICloudAsset = YES;
            }
        }]; 
    }];
}];

2
2017-08-13 02:32



我想将PHAssets对象添加到我的数据源中,只要它在iCloud中。这是我必须要求的图像,然后我可以知道它在iCloud中 - rishu1992
没有其他方法可以知道资产是否在云中。如果您有性能问题,那么您可以要求一个非常小的资产或预取资产。 - jarora
在我的视频测试中 requestImageForAsset: 没有回来 PHImageResultIsInCloudKey 在info目录中。然而 requestImageDataForAsset: 总是这样,所以我会使用那个功能。 - bcattle
每次调用requestImageForAsset时我都会得到一个信息字典;但是,为了实验,我使用requestImageDataForAsset方法只是为了看看会发生什么。如果它不适合您,可以将它替换为requestImageForAsset方法。 - James Bush


以下是一种方法,您可以实现获取Photos应用程序的Videos文件夹中的所有视频,该应用程序使用带有PHFetchRequest的谓词来仅过滤存储在iPhone本身上的视频,而不是iCloud中的视频:

// Collect all videos in the Videos folder of the Photos app
- (PHFetchResult *)assetsFetchResults {
    __block PHFetchResult *i = self->_assetsFetchResults;
    if (!i) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            PHFetchOptions *fetchOptions = [PHFetchOptions new];
            fetchOptions.predicate = [NSPredicate predicateWithFormat:@"(sourceType & %d) != 0", PHAssetSourceTypeUserLibrary];
            PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumVideos options:fetchOptions];
            PHAssetCollection *collection = smartAlbums.firstObject;
            if (![collection isKindOfClass:[PHAssetCollection class]]) collection = nil;
            PHFetchOptions *allPhotosOptions = [[PHFetchOptions alloc] init];
            allPhotosOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
            i = [PHAsset fetchAssetsInAssetCollection:collection options:allPhotosOptions];
            self->_assetsFetchResults = i;
        });
    }

    return i;
}

Apple关于PHFetchResult的文档指出,只有一部分属性可以与谓词一起使用;所以,如果上面的代码不适合你,删除PHFetchOptions谓词,并将PHFetchRequest中相应的引用替换为nil:

// Collect all videos in the Videos folder of the Photos app
- (PHFetchResult *)assetsFetchResults {
    __block PHFetchResult *i = self->_assetsFetchResults;
    if (!i) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumVideos options:nil];
            PHAssetCollection *collection = smartAlbums.firstObject;
            if (![collection isKindOfClass:[PHAssetCollection class]]) collection = nil;
            PHFetchOptions *allPhotosOptions = [[PHFetchOptions alloc] init];
            allPhotosOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
            i = [PHAsset fetchAssetsInAssetCollection:collection options:allPhotosOptions];
            self->_assetsFetchResults = i;
        });
    }

    return i;
}

然后,添加以下行:

// Filter videos that are stored in iCloud
- (NSArray *)phAssets {
    NSMutableArray *assets = [NSMutableArray arrayWithCapacity:self.assetsFetchResults.count];
    [[self assetsFetchResults] enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) {
        if (asset.sourceType == PHAssetSourceTypeUserLibrary)
                 [assets addObject:asset];
     }];

    return [NSArray arrayWithArray:(NSArray *)assets];
}

0
2017-07-14 20:19





这段代码应该可行。 如果非常频繁地调用此代码,请确保通过PHImageRequestID取消无用的请求。

- (PHImageRequestID)checkIsCloud:(PHAsset *)asset cachingImageManager:(PHCachingImageManager *)cachingImageManager {
    if (asset.mediaType == PHAssetMediaTypeVideo) {
        PHVideoRequestOptions *options = [PHVideoRequestOptions new];
        options.deliveryMode = PHVideoRequestOptionsDeliveryModeMediumQualityFormat;
        return [cachingImageManager requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset * _Nullable avAsset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
            if (asset != self.asset) return;
            dispatch_async(dispatch_get_main_queue(), ^{
                if (info[@"PHImageFileSandboxExtensionTokenKey"]) {
                    self.iCloudStatus = KICloudStatusNone;
                } else if ([info[PHImageResultIsInCloudKey] boolValue]) {
                    self.iCloudStatus = KICloudStatusNormal;
                } else {
                    self.iCloudStatus = KICloudStatusNone;
                }
            });
        }];
    } else {
        return [cachingImageManager requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
            if (asset != self.asset) return;
            dispatch_async(dispatch_get_main_queue(), ^{
                if ([info[PHImageResultIsInCloudKey] boolValue]) {
                    self.iCloudStatus = KICloudStatusNormal;
                } else {
                    self.iCloudStatus = KICloudStatusNone;
                }
            });
        }];
    }
}

0
2018-05-21 12:19