前言
上一篇大致介绍了photos的基本使用,这一篇介绍获取视频文件,LivePhoto文件的方法以及其它常用的一些api.
获取视频资源
第一种方法
首先判断一下PHAsset类的类型,判断它是否为视频类型,然后获取视频文件的路径,然后可以通过这个路径来获取文件,也可以通过文件读写导出一个副本:
大致代码如下
if (_asset.mediaType == PHAssetMediaTypeVideo) { //判断asset是不是视频
[[PhotosTool sharedTool] requestVedioWithAsset:_asset andCompletionHandler:nil];
}
下面是获取视频文件路径,然后导出视频文件.
- (void)requestVedioWithAsset:(PHAsset *)asset andCompletionHandler:(void (^)(NSString *))handler {
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
[[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
if ([asset isKindOfClass:[AVURLAsset class]]) {
NSURL *url = ((AVURLAsset *)asset).URL;
[self writeDataWithFilePath:url.absoluteString];
}
}];
}
- (void)writeDataWithFilePath:(NSString *)filePath {
NSString *outPath = @"/Users/rjkfb2/Desktop/temp.mov";
NSString *readPath = filePath;
if ([readPath hasPrefix:@"file://"]) {
readPath = [readPath stringByReplacingOccurrencesOfString:@"file://" withString:@""];
}
NSFileHandle *readHandle = [NSFileHandle fileHandleForReadingAtPath:readPath];
NSFileHandle *writeHandle = [NSFileHandle fileHandleForWritingAtPath:outPath];
NSData *data = nil;
while (1) {
data = [readHandle readDataOfLength:1024];
if (data != nil && data.length > 0) {
[writeHandle writeData:data];
}else {
break;
}
}
[readHandle closeFile];
[writeHandle closeFile];
}
注意:使用完文件后要删除文件.
第二种获取视频文件的方法
- (void)requestVideoWithAsset:(PHAsset *)asset andCompletionHandler:(void (^)(NSString *))handler {
if (asset.mediaType == PHAssetMediaTypeVideo) {
PHAssetResource *one = nil;
NSArray *arr = [PHAssetResource assetResourcesForAsset:asset];
for (PHAssetResource *resource in arr) {
if (resource.type == PHAssetResourceTypeVideo) {
break;
}
}
PHAssetResourceRequestOptions *options = [[PHAssetResourceRequestOptions alloc] init];
options.networkAccessAllowed = YES;//有可能资源得从icloud下载
options.progressHandler = ^(double progress) {//监测进度
};
NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:one.originalFilename];
[[PHAssetResourceManager defaultManager] writeDataForAssetResource:one toFile:[NSURL URLWithString:filePath] options:options completionHandler:^(NSError * _Nullable error) {
if (error == nil) {
NSLog(@"写入文件成功----");
}
}];
}
}
注:这里是通过PHAssetResource 和 PHAssetResourceManager这两个类来导出asset对应的文件数据,这里隐藏了asset对应的资源文件的文件路径.
获取LivePhoto文件
LivePhoto从字面上理解就是动态照片,那我们怎么读取这一类文件呢,并且加载这类文件呢.首先我们先看一下它对应的文件结构.
- (void)requestLivePhotoWithAsset:(PHAsset *)asset andCompletion:(void (^)(PHLivePhoto *))handler {
if (asset.mediaSubtypes == PHAssetMediaSubtypePhotoLive) {//判断为LivePhoto
NSArray *arr = [PHAssetResource assetResourcesForAsset:asset];
NSLog(@"--------%@",arr);
}
}
打印结果如下图:
我们发现一个LivePhoto包含两个文件,一个是图片文件,一个是视频文件,所以LivePhoto不能作为一个整体文件上传到服务器,但是可以获它对应的视频文件和图片文件,来上传服务器,下面代码是获取LivePhoto对应的文件数据:
- (void)requestDataForLivePhotoWithAsset:(PHAsset *)asset andCompletion:(void (^)(NSString *, NSString *))completion {
__block void (^handler)(NSString *, NSString *) = completion;
NSString *imagePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *videoPath = imagePath;
PHAssetResource *imageResource = nil;
PHAssetResource *videoResource = nil;
NSArray *resources = [PHAssetResource assetResourcesForAsset:asset];
for (PHAssetResource *resource in resources) {
if (resource.type == PHAssetResourceTypePairedVideo) {//LivePhoto
videoResource = resource;
}else if (resource.type == PHAssetResourceTypePhoto) {//image
imageResource = resource;
}
}
imagePath = [imagePath stringByAppendingPathComponent:@"one.jpg"];
videoPath = [videoPath stringByAppendingPathComponent:@"one.mov"];
[[NSFileManager defaultManager] removeItemAtPath:imagePath error:nil];
[[NSFileManager defaultManager] removeItemAtPath:videoPath error:nil];
PHAssetResourceRequestOptions *options = [[PHAssetResourceRequestOptions alloc] init];
options.networkAccessAllowed = YES;
[[PHAssetResourceManager defaultManager] writeDataForAssetResource:imageResource toFile:[NSURL fileURLWithPath:imagePath] options:options completionHandler:^(NSError * _Nullable error) {
if (error == nil) {
PHAssetResourceRequestOptions *options1 = [[PHAssetResourceRequestOptions alloc] init];
options1.networkAccessAllowed = YES;
[[PHAssetResourceManager defaultManager] writeDataForAssetResource:videoResource toFile:[NSURL fileURLWithPath:videoPath] options:options1 completionHandler:^(NSError * _Nullable error) {
if (error == nil) {
handler(imagePath,videoPath);
}else {
handler(nil,nil);
}
}];
}else {
handler(nil,nil);
}
}];
}
注:这里会把视频文件和图片文件写入到Documents文件夹,然后返回文件路径,可以根据这个路径获取文件数据,然后上传服务器,那么如何保存 LivePhoto,对于支持 LivePhoto 的手机用户可能需要将 LivePhoto 保存到手机相册。但是事实上 LivePhoto 不能作为一个整体文件存在于内存硬盘或者服务器。但是可以将一个视频文件和图片文件一起作为 LivePhoto Asset 保存到相册.
LivePhoto的保存
- (void)saveLivePhotoWithVedioURL:(NSURL *)videoUrl andImageURL:(NSURL *)imageUrl andCompletion:(void (^)(PHLivePhoto *, BOOL))completion{
__block void (^handler)(PHLivePhoto *, BOOL) = completion;
__block NSString *localIdentifier = @"";
[library performChanges:^{
PHAssetCreationRequest *request = [[PHAssetCreationRequest alloc] init];
[request addResourceWithType:PHAssetResourceTypePhoto fileURL:imageUrl options:nil];
[request addResourceWithType:PHAssetResourceTypePairedVideo fileURL:videoUrl options:nil];
//获取对应的标识符,用于获取资源
localIdentifier = [request placeholderForCreatedAsset].localIdentifier;
} completionHandler:^(BOOL success, NSError * _Nullable error) {//保存成功之后,返回PHLivePhoto对象
PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:@[localIdentifier] options:nil];
__block PHAsset *asset = nil;
[result enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:PHAsset.class]) {
asset = (PHAsset *)obj;
if (asset.mediaSubtypes == PHAssetMediaSubtypePhotoLive) {
[[PHImageManager defaultManager] requestLivePhotoForAsset:asset targetSize:CGSizeMake(asset.pixelWidth, asset.pixelHeight) contentMode:PHImageContentModeAspectFill options:nil resultHandler:^(PHLivePhoto * _Nullable livePhoto, NSDictionary * _Nullable info) {
dispatch_async(dispatch_get_main_queue(), ^{
if (livePhoto) {
handler(livePhoto, YES);
handler = nil;
}else {
handler(nil,NO);
}
});
}];
}
}
}];
} ];
}
获取到LivePhoto之后,播放的话,使用系统提供的PHLivePhotoView就可以了,需要导入头文件"#import
#import "ShowVC.h"
#import "PhotosTool.h"
#import
@interface ShowVC ()
@property (weak, nonatomic) IBOutlet UIImageView *poster;
@property (nonatomic,strong) PHLivePhotoView *liveView;
@end
@implementation ShowVC
- (void)viewDidLoad {
[super viewDidLoad];
[self setupUI];
[self loadImage];
}
- (void)setupUI {
_liveView = [[PHLivePhotoView alloc] initWithFrame:CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
[self.view addSubview:_liveView];
_liveView.delegate = self;
_liveView.hidden = YES;
}
- (void)loadImage {
if (_asset.mediaSubtypes == PHAssetMediaSubtypePhotoLive) {
_liveView.hidden = NO;
PHLivePhotoRequestOptions *option = [[PHLivePhotoRequestOptions alloc] init];
option.networkAccessAllowed = YES;
[[PHImageManager defaultManager] requestLivePhotoForAsset:_asset targetSize:CGSizeMake(_asset.pixelWidth, _asset.pixelHeight) contentMode:PHImageContentModeAspectFit options:option resultHandler:^(PHLivePhoto * _Nullable livePhoto, NSDictionary * _Nullable info) {
_liveView.livePhoto = livePhoto;
[_liveView startPlaybackWithStyle:PHLivePhotoViewPlaybackStyleFull];
}];
}else {
[[PhotosTool sharedTool] requestImgaeWithSize:CGSizeMake(_asset.pixelWidth, _asset.pixelHeight) andAsset:_asset andCompletionHandler:^(UIImage *result) {
_poster.image = result;
}];
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[[PhotosTool sharedTool] requestDataForLivePhotoWithAsset:_asset andCompletion:^(NSString *imagePath, NSString *videoPath) {
}];
}
#pragma mark - PHLivePhotoViewDelegate
- (void)livePhotoView:(PHLivePhotoView *)livePhotoView willBeginPlaybackWithStyle:(PHLivePhotoViewPlaybackStyle)playbackStyle {
}
- (void)livePhotoView:(PHLivePhotoView *)livePhotoView didEndPlaybackWithStyle:(PHLivePhotoViewPlaybackStyle)playbackStyle {
}
监听相册的变化
有时候,我们需要监听相册的变化,比如添加或者删除了图片,这样我们就能及时更新图库资源.例如,第三方图片多选框架就会监测相册的变化,然后更新图库资源展示.那么具体怎么实现监听呢.第一步,遵循PHPhotoLibraryChangeObserver协议
[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
然后实现代理方法
- (void)photoLibraryDidChange:(PHChange *)changeInstance {
for (PHAssetCollection *collection in self.collections) {//遍历,查看是哪个相册变化了
if ([changeInstance changeDetailsForObject:collection]) {
}
}
}
记住最后要移除观察者
[[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:shareTool];
在发现哪个相册图片变化了之后,可以发送通知,然后在展示图片资源的控制器里接收到通知之后,刷新数据.大致就是这样一个过程.
保存图片到相册中
有时候我们需要保存图片到相册,具体过程如下
- (void)saveImageWithImage:(UIImage *)image andCompletion:(void (^)(BOOL, PHAsset *))completion {
__block void(^handler)(BOOL, PHAsset *) = completion;
__block NSString *identifier = @"";
[library performChanges:^{
PHAssetChangeRequest *request = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
identifier = [request placeholderForCreatedAsset].localIdentifier;
} completionHandler:^(BOOL success, NSError * _Nullable error) {
__block PHAsset *asset = nil;
PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:@[identifier] options:nil];
[result enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:PHAsset.class]) {
asset = (PHAsset *)obj;
dispatch_async(dispatch_get_main_queue(), ^{
if (handler) {
handler(YES , asset);
handler = nil;
}
});
return;
}
}];
}];
}
有时候我们需要把资源保存到指定的相册,那么这个时候代码如下:
//保存到指定相册
- (void)saveImageWithImage:(UIImage *)image andCollection:(PHAssetCollection *)collection andCompletion:(void (^)(BOOL, PHAsset *))completion {
__block void(^handler)(BOOL, PHAsset *) = completion;
__block NSString *identifier = @"";
[library performChanges:^{
PHAssetChangeRequest *request = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
PHAssetCollectionChangeRequest *collectionRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:collection];
[collectionRequest addAssets:@[request.placeholderForCreatedAsset]];
identifier = [request placeholderForCreatedAsset].localIdentifier;
} completionHandler:^(BOOL success, NSError * _Nullable error) {
__block PHAsset *asset = nil;
PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:@[identifier] options:nil];
[result enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([obj isKindOfClass:PHAsset.class]) {
asset = (PHAsset *)obj;
dispatch_async(dispatch_get_main_queue(), ^{
if (handler) {
handler(YES , asset);
handler = nil;
}
});
return;
}
}];
}];
}
还有新建相册:
//新建相册
- (void)createAssetsCollectionWithName:(NSString *)name {
[library performChanges:^{
PHAssetCollectionChangeRequest *request = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:name];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
}];
}
demo
上面就是Photos框架的使用,希望能给别人带来帮助.不积跬步无以至千里,也希望自己通过这些实践能够积累知识.