首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用CloudKit接口将PHLivePhoto保存到iCloud

如何使用CloudKit接口将PHLivePhoto保存到iCloud
EN

Stack Overflow用户
提问于 2016-06-30 01:11:19
回答 1查看 359关注 0票数 0

现在我正在使用下面的code.But,一些奇怪的事情正在发生。

代码语言:javascript
复制
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,然后取回。

代码语言:javascript
复制
NSData *data = [NSData dataWithContentsOfURL: fetchedAsset.fileURL];
PHLivePhoto *thePhoto = [NSKeyedUnarchiver unarchiveObjectWithData: data];
PHLivePhotoView *photoView = ......;
photoView.livePhoto = thePhoto;

它的大部分工作,除了当photoView停止播放,照片视图的图像只是disappears.If我再次长时间触摸它,它可以正常播放。

为什么会发生这种情况?

EN

回答 1

Stack Overflow用户

发布于 2016-12-27 16:35:21

看起来NSKeyedArchiver没有按照预期保存实时照片。作为解决方案之一,您可以拆卸PHLivePhoto并分别检索视频和静止图像,然后将它们上传到iCloud:

代码语言:javascript
复制
#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中检索)有一个小“诀窍”--你应该确保图像和视频在元数据中都有相同的资产ID,否则iOS不会知道这两个部分(视频和图像)属于一个复合资产--实时照片--并且无法将它们组装成一个正确的PHLivePhoto对象(然而,在这种情况下,你很可能会得到PHLivePhoto,但它将被构造为一个静止的照片,没有动画)。

这里最简单的方法是从视频资产中提取资产ID,然后通过分配相同的ID来修改图像部分:

代码语言:javascript
复制
- (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
    }];
}

根据苹果的文档,包含实时照片的结果块可能会被调用多次(有关更多信息,请参阅PHLivePhoto.h ):

将多次调用结果处理程序

,以提供具有越来越多内容的新PHLivePhoto实例。

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/38106104

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档