美文网首页
Photos框架使用(二)

Photos框架使用(二)

作者: CoderP1 | 来源:发表于2018-03-22 11:01 被阅读0次

    前言

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

    打印结果如下图:


    屏幕快照 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 <PhotosUI/PhotosUI.h>",代码如下:

    #import "ShowVC.h"
    #import "PhotosTool.h"
    #import <PhotosUI/PhotosUI.h>
    @interface ShowVC ()<PHLivePhotoViewDelegate>
    @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<UITouch *> *)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框架使用(二)

          本文链接:https://www.haomeiwen.com/subject/wdbbqftx.html