Проверьте, что данный 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?
7 ответов
Это что-то вроде взлома, когда мне пришлось выкопать массив ресурсов и отладить, чтобы найти необходимую информацию. Но это работает. Хотя это недокументированный код, и я не уверен, что Apple отклонит приложение из-за этого или нет. Попробуйте и посмотрите, что получится!
// 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-адрес файла и т. Д.
На самом деле существует 2 типа ситуаций: 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.
}
});
- Фотография находится в iCloud, но загружена с другого устройства. И вы не сохранили его в своей местной фото-библиотеке. Таким образом, блок progressHandler никогда не будет вызываться. Я не знаю почему, но это правда, и я думаю, что это своего рода ошибка в фреймворке PhotoKit. В этой ситуации, если вы используете PHImageResultIsInCloudKey, это также сложно. Потому что вы можете знать значение PHImageResultIsInCloudKey только в блоке resultHandler requestImageForAsset. Но это время после запроса фотографии.
Так что, по крайней мере, на мой взгляд, нет способа проверить, хранится ли фотография в iCloud. Может быть, есть другой лучший способ, пожалуйста, дайте мне знать. Спасибо большое!
Когда вы запрашиваете изображение, вы получаете ключ в информационном словаре, который сообщает вам, присутствует ли актив в 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;
}
}];
}];
}];
Вот версия Swift 3
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!)
}
}
}
}
Ниже приведен метод, который можно реализовать для получения всех видео в папке "Видео" приложения "Фотографии", в котором используется предикат с 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];
}
Вот версия Swift 5
extension PHAsset {
var isLocallyAvailable: Bool? {
let resourceArray = PHAssetResource.assetResources(for: self)
return resourceArray.first?.value(forKey: "locallyAvailable") as? Bool
}
}
Этот код должен работать. Если этот код вызывается очень часто, обязательно отмените бесполезный запрос по 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;
}
});
}];
}
}