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を保存してください
などにエラーのために必要なすべてのチェックを(それらのかなりが、実際に、そこにある)とハンドラを追加する必要があることを心に留めておいてください? –
PHAssetResourcesをiCloudに保存するにはどうすればよいですか?すべてのヒント?ありがとう。 –
私はNSKeyedArchiverとNSKeyedUnarchiverがこのバグに責任があると考えました。私はそれらのないURLにPHLivePhotoを書き込もうとしています。 –