2016-06-29 2 views
0

Сейчас я использую следующий код. Но что-то странное происходит.Как сохранить PHLivePhoto в iCloud с помощью CloudKit API

PHLivePhoto *livePhoto = .....; 

NSString *fileName = [[NSProcessInfo processInfo] globallyUniqueString]; 
NSURL *url = [NSURL fileURLWithPath: NSTemporaryDirectory()]; 
url = [url URLByAppendingPathComponent: fileName]; 
NSData *data = [NSKeyedArchiver archivedDataWithRootObject: livePhoto]; 
[data writeToURL: url atomically: YES]; 

CKAsset *asset = [[CKAsset alloc] initWithFileURL: url]; 

И затем использовать CKModifyRecordsOperation, чтобы сохранить этот актив ICloud, а затем забрать обратно.

NSData *data = [NSData dataWithContentsOfURL: fetchedAsset.fileURL]; 
PHLivePhoto *thePhoto = [NSKeyedUnarchiver unarchiveObjectWithData: data]; 
PHLivePhotoView *photoView = ......; 
photoView.livePhoto = thePhoto; 

Большая часть его работ, за исключением того, что когда PhotoView прекращает воспроизведение, образ PhotoView просто disappears.If я давно прикоснуться к ней снова, она воспроизводит нормально.

Почему это происходит?

+1

Может быть, попробуйте сохранить отдельные 'PHAssetResource's вместо этого? –

+0

Как сохранить PHAssetResources в iCloud? Любые советы? Спасибо. –

+0

Я выяснил, что NSKeyedArchiver и NSKeyedUnarchiver виноваты в этой ошибке. Я пытаюсь написать PHLivePhoto для URL без них. –

ответ

1

Похоже, что NSKeyedArchiver не сохраняет изображение в прямом эфире, как ожидалось. В качестве одного из решений, вы должны демонтировать PHLivePhoto и получить отдельно видео и неподвижных изображений, а затем загрузить их в ICloud:

#import <Photos/Photos.h> 
#import <CloudKit/CloudKit.h> 
#import <MobileCoreServices/MobileCoreServices.h> 

+ (void) disassembleLivePhoto:(nonnull PHLivePhoto*)livePhoto completion:(void (^__nonnull)(UIImage * _Nullable stillImage, AVURLAsset * _Nullable video, NSURL* _Nullable imageURL, NSURL* _Nullable videoURL))block 
{ 
    NSArray<PHAssetResource*>* resources = [PHAssetResource assetResourcesForLivePhoto:livePhoto]; 

    __block PHAssetResource *resImage = nil, *resVideo = nil; 
    NSString *fileName = [[NSUUID UUID] UUIDString]; 
    NSURL *urlMov = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[fileName stringByAppendingPathExtension:@"mov"]]]; 
    NSURL *urlImg = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[fileName stringByAppendingPathExtension:@"jpg"]]]; 

    [resources enumerateObjectsUsingBlock:^(PHAssetResource * _Nonnull res, NSUInteger idx, BOOL * _Nonnull stop) { 
     if (res.type == PHAssetResourceTypePairedVideo){ 
      resVideo = res; 
     } else if (res.type == PHAssetResourceTypePhoto){ 
      resImage = res; 
     } 
    }]; 

    [[PHAssetResourceManager defaultManager] writeDataForAssetResource:resVideo toFile:urlMov options:nil completionHandler:^(NSError * _Nullable error) {  
     [[PHAssetResourceManager defaultManager] writeDataForAssetResource:resImage toFile:urlImg options:nil completionHandler:^(NSError * _Nullable error) { 
      block([UIImage imageWithData:[NSData dataWithContentsOfURL:urlImg]], [AVURLAsset assetWithURL:urlMov], urlImg, urlMov); 
     }]; 
    }]; 
} 

- (void) sendLivePhotoComponentsWithImageURL:(nonnull NSURL*)urlImage videoURL:(nonnull NSURL*)urlVideo completionBlock:(void(^ __nonnull)(CKRecord* __nullable recordLivePhoto))block 
{  
    CKAsset *assetVideo = [[CKAsset alloc] initWithFileURL:urlVideo]; 
    CKAsset *assetImage = [[CKAsset alloc] initWithFileURL:urlImage]; 

    CKContainer *ckcContainer = [CKContainer defaultContainer]; 
    CKDatabase *ckdbPublic = [ckcContainer publicCloudDatabase]; // in this example I use public DB 

    [ckcContainer fetchUserRecordIDWithCompletionHandler:^(CKRecordID * _Nullable ownerRecordID, NSError * _Nullable error) { 
     CKRecordID *recordID = [[CKRecordID alloc] initWithRecordName:@"your_record_name_e.g._UUID" zoneID:ownerRecordID.zoneID]; 
     CKRecord *record = [[CKRecord alloc] initWithRecordType:@"your_record_type" recordID:recordID]; 
     record[@"your_video_asset_CK_key"] = assetVideo; 
     record[@"your_image_asset_CK_key"] = assetImage; 

     CKModifyRecordsOperation * op = [[CKModifyRecordsOperation alloc] initWithRecordsToSave:@[record] recordIDsToDelete:nil]; 
     op.modifyRecordsCompletionBlock = ^void(NSArray<CKRecord *> * _Nullable savedRecords, NSArray<CKRecordID *> * _Nullable deletedRecordIDs, NSError * _Nullable operationError){ 
      block(savedRecords.firstObject); // Done. 
     }; 

     op.qualityOfService = NSQualityOfServiceUserInitiated; 

     [ckdbPublic addOperation:op]; 
    }]; 
} 

Вторая часть (извлечение из ICloud) имеет немного «трюк» - вы должны убедиться, что оба изображения и видео имеют одинаковый идентификатор актива, содержащийся в метаданных, в противном случае iOS не будет знать, что эти две части (видео и изображение) принадлежат одному составному ресурсу - живое фото - и не смогут их собрать в один правильноPHLivePhoto объект (в этом случае, скорее всего, вы, , получите, получите PHLivePhoto, но он будет построен как фото, без анимации).

Самый простой способ здесь, чтобы извлечь идентификатор объекта из видео актива, а затем изменить часть изображения, назначив ему тот же ID:

- (void) assembleLivePhotoWithCKRecord:(nonnull CKRecord*)record completion:(void (^__nullable)(PHLivePhoto* _Nullable livePhoto))block 
{ 
    // Operational data 
    CKAsset *assetVideo = record[@"your_video_asset_CK_key"]; 
    CKAsset *assetImage = record[@"your_image_asset_CK_key"]; 

    // Get video and prepare local URLs 
    NSString *fileName = [[NSUUID UUID] UUIDString]; 
    NSString *pathVideo = [NSTemporaryDirectory() stringByAppendingPathComponent:[fileName stringByAppendingPathExtension:@"mov"]]; 
    NSString *pathImage = [NSTemporaryDirectory() stringByAppendingPathComponent:[fileName stringByAppendingPathExtension:@"jpg"]]; 
    NSURL *urlVideo = [NSURL fileURLWithPath:pathVideo]; 
    NSURL *urlImage = [NSURL fileURLWithPath:pathImage]; 

    NSData *dataVideo = [NSData dataWithContentsOfURL:assetVideo.fileURL]; 
    [[NSFileManager defaultManager] createFileAtPath:pathVideo contents:dataVideo attributes:nil]; 

    // Getting video asset ID from metadata 
    NSString *metaID = nil; 
    NSArray<AVMetadataItem*>* metadata = [[AVURLAsset assetWithURL:urlVideo] metadata]; 
    for (AVMetadataItem *md in metadata){ 
     if ([md.identifier containsString:@"com.apple.quicktime.content.identifier"]){ 
      metaID = (NSString*)(md.value); 
      break; 
     } 
    } 

    // Get image 
    NSData *dataImage = [NSData dataWithContentsOfURL:assetImage.fileURL]; 
    UIImage *image = [UIImage imageWithData:dataImage]; 
    CGImageRef ref = [image CGImage]; 

    // Update image's metadata to make it conform video metadata 
    NSDictionary *imgMetadata = @{@"{MakerApple}": @{@"17": metaID}}; 
    NSMutableData *imageData = [NSMutableData new]; 
    CGImageDestinationRef dest = CGImageDestinationCreateWithData((CFMutableDataRef)imageData, kUTTypeJPEG, 1, nil); 
    CGImageDestinationAddImage(dest, ref, (CFDictionaryRef)imgMetadata); 
    CGImageDestinationFinalize(dest); 

    [imageData writeToFile:pathImage atomically:YES]; 


    [PHLivePhoto requestLivePhotoWithResourceFileURLs:@[urlImage, urlVideo] placeholderImage:nil targetSize:CGSizeZero contentMode:PHImageContentModeAspectFit resultHandler:^(PHLivePhoto * _Nullable livePhoto, NSDictionary * _Nonnull info) { 
     block(livePhoto);  // NOTE: this block may be called several times 
    }]; 
} 

В результате блок с живой фотографией можно назвать несколько раз в документации компании Apple (см PHLivePhoto.h для получения дополнительной информации):

обработчика результат будет вызываться несколько раз, чтобы поставить новые экземпляры PHLivePhoto с более большим содержанием.

Кроме того, имейте в виду, что вы должны добавить все необходимые проверки (Существует совсем немного из них, на самом деле) и обработчики ошибок и т.д.

 Смежные вопросы

  • Нет связанных вопросов^_^