Photos框架使用(二)

前言

 上一篇大致介绍了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);
    }
}

打印结果如下图:


Photos框架使用(二)_第1张图片
屏幕快照 2018-03-23 下午3.55.25.png

我们发现一个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框架的使用,希望能给别人带来帮助.不积跬步无以至千里,也希望自己通过这些实践能够积累知识.

你可能感兴趣的:(Photos框架使用(二))