Live Photo
Live Photo 是一种由苹果公司推出的照片格式,它能够捕捉和展示静态照片之外的动态内容。当用户拍摄一张 Live Photo时,相机会在用户按下快门的瞬间记录几秒钟的视频和音频。这使得照片变得更加生动,用户可以通过长按照片来观看和感受其中的动态内容。
用白话说就是拍照时的实况,拍摄的照片长按预览时可以看到 3秒的视频效果。这里记录一下 Live Photo 的基本使用,末尾附完整示例demo。
Live Photo的拍摄与区分
拍摄 Live Photo也很简单,打开苹果相机选择 【照片】开启顶部的【实况】按钮即可,拍摄完成后在相册中就可以看到照片左上角会带【实况】标志。
Live Photo的基本使用
从本地获取 Live Photo 主要有3种情况:
- 系统相册:使用照片选择器 UIImagePickerController直接获取Live Photo 照片
- 自定义相册:获取照片的资源文件 PHAsset 再转为 Live Photo
- 本地文件:从本地沙盒或者 main bundle 获取
- 网络 Live Photo
使用UIImagePickerController
1.导入依赖
#import <Photos/Photos.h>
#import <PhotosUI/PhotosUI.h>
#import <MobileCoreServices/MobileCoreServices.h>
2.请求权限
PHAuthorizationStatus authstatus = [PHPhotoLibrary authorizationStatus];
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status == PHAuthorizationStatusAuthorized) {
//允许使用相册
dispatch_async(dispatch_get_main_queue(), ^{
[self chooseMovFromLibrary];
});
}else if(authstatus != PHAuthorizationStatusNotDetermined) {
dispatch_async(dispatch_get_main_queue(), ^{
...
});
}
}];
3.实现图片选择协议
- (void)chooseMovFromLibrary {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
// 选取live photo
imagePicker.mediaTypes = @[(NSString *)kUTTypeImage,(NSString *)kUTTypeLivePhoto];
imagePicker.delegate = self;
[self presentViewController:imagePicker animated:YES completion:^{}];
}
#pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
// 从选择器返回的信息中获取 Live Photo 对象
PHLivePhoto *photo = [info objectForKey:UIImagePickerControllerLivePhoto];
// dismiss 图片选择器界面
[picker dismissViewControllerAnimated:YES completion:^{
if (photo) {
self.livePhotoView.hidden = NO;
// 将 Live Photo 对象赋值给 PHLivePhotoView 对象,以便显示
self.livePhotoView.livePhoto = photo;
}
}];
}
4.设置播放暂停
Live Photo 有3种播放模式,根据自己需求选择
typedef NS_ENUM(NSInteger, PHLivePhotoViewPlaybackStyle) {
PHLivePhotoViewPlaybackStyleUndefined = 0, // 不播放
PHLivePhotoViewPlaybackStyleFull, // 完全播放
PHLivePhotoViewPlaybackStyleHint, // 预览播放
};
// 设置播放
- (void)playButtonClick {
[self.livePhotoView startPlaybackWithStyle: PHLivePhotoViewPlaybackStyleFull];
}
// 暂停播放
- (void)stopButtonClick {
[self.livePhotoView stopPlayback];
}
使用PHAsset转为Live Photo
很多时候我们获取到的会是 PHAsset资源,这时就需要对资源类型进行转换,通过 PHImageManager 的**- (PHImageRequestID)requestLivePhotoForAsset:targetSize:contentMode:options:resultHandler:
**转换为 Live Photo
PHFetchOptions *options = [[PHFetchOptions alloc] init];
// 按创建日期降序排列
options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
// 获取资产(照片或视频)列表
PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
// 查找一张live photo
__block PHAsset *asset;
[assetsFetchResults enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass: [PHAsset class]]) {
PHAsset *tmpAsset = (PHAsset*)obj;
if (tmpAsset.mediaSubtypes == PHAssetMediaSubtypePhotoLive) {
asset = tmpAsset;
*stop = YES;
}
}
NSLog(@"%@", obj);
}];
if (asset) {
// PHAsset 转 live photo
[[PHImageManager defaultManager] requestLivePhotoForAsset:asset
targetSize:CGSizeMake(asset.pixelWidth, asset.pixelHeight)
contentMode:PHImageContentModeAspectFit
options:nil
resultHandler:^(PHLivePhoto * _Nullable livePhoto, NSDictionary * _Nullable info) {
if (livePhoto) {
self.livePhotoView.hidden = NO;
// 将获取到的 Live Photo 设置给 PHLivePhotoView
self.livePhotoView.livePhoto = livePhoto;
}
}];
}
本地文件
本地沙盒一般存放的是图片、视频或者json等类型明确的文件,Live Photo 比较特殊,是一张图片加一个 mov 视频,并且该图片和视频必须来自同一张 Live Photo。
因此我们需要从沙盒中分别获取到图片和视频的URL地址,然后调用 PHLivePhoto 的 +(PHLivePhotoRequestID)requestLivePhotoWithResourceFileURLs:placeholderImage:targetSize:contentMode:resultHandler:
进行获取
- (void)loadLocalFile {
NSString *imagePath = [self getFilePathWithKey:@"image.jpg"];
NSString *movPath = [self getFilePathWithKey:@"mov.mov"];
// 创建一个 NSURL 数组,包含 Live Photo 的静态图片和视频文件的 URL
NSArray *resourceFileURLs = @[
[NSURL fileURLWithPath:imagePath],
[NSURL fileURLWithPath:movPath],
];
// 创建一个占位图 UIImage 对象
UIImage *placeholderImage = [UIImage imageNamed:@"IMG_1232.JPG"];
// 请求一个 Live Photo 对象
[PHLivePhoto requestLivePhotoWithResourceFileURLs:resourceFileURLs
placeholderImage:placeholderImage
targetSize:self.livePhotoView.bounds.size
contentMode:PHImageContentModeAspectFill
resultHandler:^(PHLivePhoto * _Nullable livePhoto, NSDictionary * _Nonnull info) {
if (livePhoto) {
self.livePhotoView.hidden = NO;
// 如果成功获取到 Live Photo 对象,则将其赋值给 PHLivePhotoView
self.livePhotoView.livePhoto = livePhoto;
}
// 开始以全屏播放样式播放 Live Photo
// [self.livePhotoView startPlaybackWithStyle:PHLivePhotoViewPlaybackStyleFull];
}];
}
网络资源
对于网络的 Live Photo 需要先保存到本地,然后通过 PHPhotoLibrary 的 - (void)performChanges: completionHandler:
方法保存到相册。
// 写入相册
NSString *imagePath = [self getFilePathWithKey:@"image.jpg"];
NSString *movPath = [self getFilePathWithKey:@"mov.mov"];
PHAuthorizationStatus authstatus = [PHPhotoLibrary authorizationStatus];
if (authstatus == PHAuthorizationStatusAuthorized) { //已经授权
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetCreationRequest * request = [PHAssetCreationRequest creationRequestForAsset];
[request addResourceWithType:PHAssetResourceTypePhoto fileURL:[NSURL URLWithString:imagePath] options:nil];
[request addResourceWithType:PHAssetResourceTypePairedVideo fileURL:[NSURL URLWithString:movPath] options:nil];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
if (success) {
NSLog(@"保存成功");
}
}];
}
效果图
最后附上两张效果图
项目链接
代码量有点多,放到了github上,需要的自取
- https://github.com/MisterZhouZhou/My3DTouch/blob/main/My3DTouch/LivePhotoPickerViewController.m
- https://github.com/MisterZhouZhou/My3DTouch/blob/main/My3DTouch/LocalFielViewController.m
- https://github.com/MisterZhouZhou/My3DTouch/blob/main/My3DTouch/PHAssetsViewController.m