Check given PHAsset is iCloud asset?

2019-03-15 07:32发布

I'm trying to get PhAsset object. I want to segregate iCloud assets. Here is my code,

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 

         }];
    }

}];

Please tell me how to find the PHAsset is iCloud asset?

6条回答
Fickle 薄情
2楼-- · 2019-03-15 08:12

Here is the Swift 3 version

func checkVideoType(){
    if selectedAsset != nil {
        guard (selectedAsset.mediaType == .video) else {
            print("Not a valid video media type")
            return
        }
        requestID = checkIsiCloud(assetVideo:selectedAsset, cachingImageManager: catchManager)
    }
}

 func checkIsiCloud(assetVideo:PHAsset,cachingImageManager:PHCachingImageManager) -> PHImageRequestID{

        let opt=PHVideoRequestOptions()
        opt.deliveryMode = .mediumQualityFormat
        opt.isNetworkAccessAllowed=true //iCloud video can play
        return cachingImageManager.requestAVAsset(forVideo:assetVideo, options: opt) { (asset, audioMix, info) in

            DispatchQueue.main.async {
                if (info!["PHImageFileSandboxExtensionTokenKey"] != nil) {
                    self.iCloudStatus=false
                    self.playVideo(videoAsset:asset!)
                }else if((info![PHImageResultIsInCloudKey]) != nil) {
                    self.iCloudStatus=true

                }else{
                   self.iCloudStatus=false
                   self.playVideo(videoAsset:asset!)
                }
            }
    }

}
查看更多
趁早两清
3楼-- · 2019-03-15 08:15

this code should be work. If call this code very frequently, make sure cancel useless request by 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;
                }
            });
        }];
    }
}
查看更多
Rolldiameter
4楼-- · 2019-03-15 08:17

There are actually 2 kinds of situations: 1. The photo is captured by this device, and is uploaded to iCloud. Then, you can use the progressHandler to check whether it needs iCloud download.

__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. The photo is in iCloud but uploaded from other device. And you did not save it to your local photo library. So the progressHandler block will never ever be invoked. I don't know why but it's true, and I think it's kind of a bug of PhotoKit framework. For this situation, if you use the PHImageResultIsInCloudKey, that is also difficult. Because you can know the PHImageResultIsInCloudKey value just in the requestImageForAsset's resultHandler block. But that's the time after the photo request is initiated.

So, at least, in my opinion, there is no way to check whether photo is stored in iCloud. Maybe there is other better way, please let me know. Thanks very much!

查看更多
家丑人穷心不美
5楼-- · 2019-03-15 08:28

When you request for an image you get a key in info dictionary which tells you if the asset is present in 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;
            }
        }]; 
    }];
}];
查看更多
聊天终结者
6楼-- · 2019-03-15 08:29

Following is a method you can implement to acquire all videos in the Videos folder of the Photos app, which uses a predicate with the PHFetchRequest to filter only videos stored on the iPhone itself, and not in 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's documentation on PHFetchResult states that only a subset of attributes can be used with a predicate; so, if the above code does not work for you, remove the PHFetchOptions predicate, and replace the corresponding reference in the PHFetchRequest to 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;
}

Then, add this line:

// 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];
}
查看更多
戒情不戒烟
7楼-- · 2019-03-15 08:33

It's a bit kind of hack, where I had to dig out the resource array and debug to find out my required information. But it works. Although this is an undocumented code and I'm not sure whether apple will reject the app because of this or not. Give it a try and see what happens!

// 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

You can also get some other useful information from asset resource, such as - original filename, file size, file url, etc.

查看更多
登录 后发表回答