Check given PHAsset is iCloud asset?

后端 未结 6 1166
再見小時候
再見小時候 2021-02-02 14:16

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

PHFetchResult *cloudAlbums = [PHAssetCollection fetchAssetCollectionsWithT         


        
相关标签:
6条回答
  • 2021-02-02 14:54

    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!)
                    }
                }
        }
    
    }
    
    0 讨论(0)
  • 2021-02-02 14:55

    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;
                }
            }]; 
        }];
    }];
    
    0 讨论(0)
  • 2021-02-02 14:59

    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!

    0 讨论(0)
  • 2021-02-02 15:11

    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];
    }
    
    0 讨论(0)
  • 2021-02-02 15:13

    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;
                    }
                });
            }];
        }
    }
    
    0 讨论(0)
  • 2021-02-02 15:14

    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.

    0 讨论(0)
提交回复
热议问题