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);
    }
}

打印结果如下图:


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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容