Ios 如何使用CloudKit API将PHLivePhoto保存到iCloud

Ios 如何使用CloudKit API将PHLivePhoto保存到iCloud,ios,icloud,cloudkit,phlivephoto,Ios,Icloud,Cloudkit,Phlivephoto,现在我正在使用下面的代码。但是一些奇怪的事情正在发生 PHLivePhoto *livePhoto = .....; NSString *fileName = [[NSProcessInfo processInfo] globallyUniqueString]; NSURL *url = [NSURL fileURLWithPath: NSTemporaryDirectory()]; url = [url URLByAppendingPathComponent: fileName]; NSDa

现在我正在使用下面的代码。但是一些奇怪的事情正在发生

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的图像就消失了。如果我再长时间触摸它,它就会正常播放


为什么会发生这种情况?

似乎
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];
    }];
}
根据苹果公司的文档,可能会多次调用带有实时照片的结果块(有关更多信息,请参见
PHLivePhoto.h
):

结果处理程序将被多次调用,以交付内容越来越多的新PHLivePhoto实例


另外,请记住,您应该添加所有必要的检查(实际上有相当多的检查)和错误处理程序等。

看来
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];
    }];
}
根据苹果公司的文档,可能会多次调用带有实时照片的结果块(有关更多信息,请参见
PHLivePhoto.h
):

结果处理程序将被多次调用,以交付内容越来越多的新PHLivePhoto实例


另外,请记住,您应该添加所有必要的检查(实际上有相当多的检查)和错误处理程序等。

也许可以尝试保存单个
PHAssetResource
s?如何将PHAssetResources保存到iCloud?有什么建议吗?谢谢。我发现这个错误应该归咎于NSKeyedArchiver和NSKeyedUnachiver。我正在尝试将PHLivePhoto写入一个没有它们的url。也许可以尝试保存单个的
PHAssetResource
s?如何将PHAssetResources保存到iCloud?有什么建议吗?谢谢。我发现这个错误应该归咎于NSKeyedArchiver和NSKeyedUnachiver。我正在尝试在没有它们的情况下将PHLivePhoto写入url。