2016-06-29 8 views
0

今、私は次のコードを使用していますが、何か不思議なことが起こっています。CloudKit APIを使用してPHLivePhotoをiCloudに保存するには

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 Iロングタッチの再生を停止したとき、それのほとんどはことを除いて、動作します。

どうしてですか?

+1

などにエラーのために必要なすべてのチェックを(それらのかなりが、実際に、そこにある)とハンドラを追加する必要があることを心に留めておいてください? –

+0

PHAssetResourcesをiCloudに保存するにはどうすればよいですか?すべてのヒント?ありがとう。 –

+0

私はNSKeyedArchiverとNSKeyedUnarchiverがこのバグに責任があると考えました。私はそれらのないURLにPHLivePhotoを書き込もうとしています。 –

答えて

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からの検索を)少し「トリック」を持っている - あなたは、両方のことを確認してください画像と動画のメタデータに同じアセットIDが含まれていると、iOSはこれらの2つの部分(動画と画像)が1つの複合アセット(ライブ写真)に属していることを認識しなくなり、をに正確に組み立てることができなくなります。PHLivePhotoオブジェクトしかし、この場合、ほとんどの場合、になります。PHLivePhotoが得られますが、静止画として、アニメーションなしで作成されます)。

ここで最も簡単な方法は、ビデオ資産から資産IDを抽出し、それを同じ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インスタンスを提供するために複数回呼び出されます。

また、たぶん代わりに個々 `PHAssetResource`sを保存してください

関連する問題