美文网首页iOS图形处理相关iOS大咖说音频与视频相关
iOS 获取系统相册内视频与图片(AssetsLibrary篇)

iOS 获取系统相册内视频与图片(AssetsLibrary篇)

作者: Snoopy008 | 来源:发表于2016-07-04 10:49 被阅读4888次

    好些天没写点东西了,最近公司要做新项目,有点小忙。不想我的坚持就此中断,我把我前些天研究的东西拿出来给大家看看。
    这次整理的是AssetsLibrary和PhotoKit的使用。本人处女座,有点强迫症,之前写的项目里用的是AssetsLibrary写的调取相册内的媒体文件,但是Xcode总是报警告错误,虽然能够编译并展示效果,但是十几个警告错误挂在那,心里总不是滋味,所以我就研究了一下AssetLibrary和PhotoKit。
    在 iOS 8 出现之前,开发者只能使用 AssetsLibrary 框架来访问设备的照片库,这是一个有点跟不上 iOS 应用发展步伐以及代码设计原则但确实强大的框架,考虑到 iOS7 仍占有不少的渗透率,因此 AssetsLibrary 也是本文重点介绍的部分。而在 iOS8 出现之后,苹果提供了一个名为 PhotoKit 的框架,一个可以让应用更好地与设备照片库对接的框架。

    一、AssetsLibrary 组成##

    AssetsLibrary 的组成比较符合照片库本身的组成,照片库中的完整照片库对象、相册、相片都能在 AssetsLibrary 中找到一一对应的组成,这使到 AssetsLibrary 的使用变得直观而方便。想要了解AssetsLibrary得从它的类开始。

    AssetsLibrary: 代表整个设备中的资源库(照片库),通过 AssetsLibrary 可以获取和包括设备中的照片和视频

    • ALAssetsGroup: 映射照片库中的一个相册,通过 ALAssetsGroup 可以获取某个相册的信息,相 册下的资源,同时也可以对某个相册添加资源。
    • ALAsset: 映射照片库中的一个照片或视频,通过 ALAsset 可以获取某个照片或视频的详细信息, 或者保存照片和视频。
    • ALAssetRepresentation: ALAssetRepresentation 是对 ALAsset 的封装(但不是其子类),可以更方便地获取 ALAsset 中的资源信息,每个 ALAsset 都有至少有一个 ALAssetRepresentation 对象,可以通过 defaultRepresentation 获取。而例如使用系统相机应用拍摄的 RAW + JPEG 照片,则会有两个 ALAssetRepresentation,一个封装了照片的 RAW 信息,另一个则封装了照片的 JPEG 信息。

    二、AssetsLibrary 的基本使用##

    AssetsLibrary 的功能很多,基本可以分为对资源的获取/保存两个部分,保存的部分相对简单,API 也比较少,因此这里不作详细介绍。获取资源的 API 则比较丰富了,一个常见的使用大量 AssetsLibrary API 的例子就是图片选择器(ALAsset Picker)。要制作一个图片选择器,思路应该是获取照片库-列出所有相册-展示相册中的所有图片-预览图片大图。
    首先是App 照片操作授权:

    NSString *tipTextWhenNoPhotosAuthorization; // 提示语
    // 获取当前应用对照片的访问授权状态
    ALAuthorizationStatus authorizationStatus = [ALAssetsLibrary authorizationStatus];
    // 如果没有获取访问授权,或者访问授权状态已经被明确禁止,则显示提示语,引导用户开启授权
    if (authorizationStatus == ALAuthorizationStatusRestricted || authorizationStatus == ALAuthorizationStatusDenied) {
        NSDictionary *mainInfoDictionary = [[NSBundle mainBundle] infoDictionary];
        NSString *appName = [mainInfoDictionary objectForKey:@"CFBundleDisplayName"];
        tipTextWhenNoPhotosAuthorization = [NSString stringWithFormat:@"请在设备的\"设置-隐私-照片\"选项中,允许%@访问你的手机相册", appName];
        // 展示提示语
    }
    

    如果已经获取授权,则可以获取相册列表:

    _assetsLibrary = [[ALAssetsLibrary alloc] init];
    _albumsArray = [[NSMutableArray alloc] init];
    [_assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
        if (group) {
            [group setAssetsFilter:[ALAssetsFilter allPhotos]];
            if (group.numberOfAssets > 0) {
                // 把相册储存到数组中,方便后面展示相册时使用
                [_albumsArray addObject:group];
            }
        } else {
            if ([_albumsArray count] > 0) {
                // 把所有的相册储存完毕,可以展示相册列表
            } else {
                // 没有任何有资源的相册,输出提示
            }
        }
    } failureBlock:^(NSError *error) {
        NSLog(@"Asset group not found!\n");
    }];
    

    上面的代码中,遍历出所有的相册列表,并把相册中资源数不为空的相册 ALAssetGroup 对象的引用储存到一个数组中。这里需要强调几点:

    • iOS 中允许相册为空,即相册中没有任何资源,如果不希望获取空相册,则需要像上面的代码中那样手动过滤。
    • ALAssetsGroup 有一个 setAssetsFilter 的方法,可以传入一个过滤器,控制只获取相册中的照片或只获取视频。一旦设置过滤,ALAssetsGroup 中资源列表和资源数量的获取也会被自动更新。
    • 整个 AssetsLibrary 中对相册、资源的获取和保存都是使用异步处理(Asynchronous),这是考虑到资源文件体积相当比较大(还可能很大)。例如上面的遍历相册操作,相册的结果使用 block 输出,如果相册遍历完毕,则最后一次输出的 block 中的 group 参数值为 nil。而 stop 参数则是用于手工停止遍历,只要把 *stop 置 YES,则会停止下一次的遍历。关于这一点常常会引起误会,所以需要注意。

    现在,已经可以获取相册了,接下来是获取相册中的资源:

    _imagesAssetArray = [[NSMutableArray alloc] init];
    [assetsGroup enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
        if (result) {
            [_imagesAssetArray addObject:result];
        } else {
            // result 为 nil,即遍历相片或视频完毕,可以展示资源列表
        }
    }];
    

    跟遍历相册的过程类似,遍历相片也是使用一系列的异步方法,其中上面的方法所输出的 block 中,除了 result 参数表示资源信息,stop 用于手工停止遍历外,还提供了一个 index 参数,这个参数表示资源的索引。一般来说,展示资源列表都会使用缩略图(result.thumbnail),因此即使资源很多,遍历资源的速度也会相当快。但如果确实需要加载资源的高清图或者其他耗时的处理,则可以利用上面的 index 参数和 stop 参数做一个分段拉取资源。例如:

    NSUInteger _targetIndex; // index 目标值,拉取资源直到这个值就手工停止拉取
    NSUInteger _currentIndex; // 当前 index,每次拉取资源时从这个值开始
     
    _targetIndex = 50;
    _currentIndex = 0;
     
    - (void)loadAssetWithAssetsGroup:(assetsGroup *)assetsGroup {
        [assetsGroup enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:_currentIndex] options:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
            _currentIndex = index;
            if (index > _targetIndex) {
                // 拉取资源的索引如果比目标值大,则停止拉取
                *stop = YES;
            } else {
                if (result) {
                    [_imagesAssetArray addObject:result];
                } else {
                    // result 为 nil,即遍历相片或视频完毕
                }
            }
        }];
    }// 之前拉取的数据已经显示完毕,需要展示新数据,重新调用 loadAssetWithAssetsGroup 方法,并根据需要更新 _targetIndex 的值
    

    最后一步是获取图片详细信息,例如:

    // 获取资源图片的详细资源信息,其中 imageAsset 是某个资源的 ALAsset 对象
    ALAssetRepresentation *representation = [imageAsset defaultRepresentation];
    // 获取资源图片的 fullScreenImage
    UIImage *contentImage = [UIImage imageWithCGImage:[representation fullScreenImage]];
    

    对于一个 ALAssetRepresentation,里面包含了图片的多个版本。最常用的是 fullResolutionImage 和 fullScreenImage。fullResolutionImage 是图片的原图,通过 fullResolutionImage 获取的图片没有任何处理,包括通过系统相册中“编辑”功能处理后的信息也没有被包含其中,因此需要展示“编辑”功能处理后的信息,使用 fullResolutionImage 就比较不方便,另外 fullResolutionImage 的拉取也会比较慢,在多张 fullResolutionImage 中切换时能明显感觉到图片的加载过程。因此这里建议获取图片的 fullScreenImage,它是图片的全屏图版本,这个版本包含了通过系统相册中“编辑”功能处理后的信息,同时也是一张缩略图,但图片的失真很少,缺点是图片的尺寸是一个适应屏幕大小的版本,因此展示图片时需要作出额外处理,但考虑到加载速度非常快的原因(在多张图片之间切换感受不到图片加载耗时),仍建议使用 fullScreenImage。

    系统相册的处理过程大概也是如上,可以看出,在整个过程中并没有使用到图片的 fullResolutionImage,从相册列表展示到最终查看资源,都是使用缩略图,这也是 iOS 相册加载快的一个重要原因。

    这里我整理一个demo,欢迎大家给予指正。demo链接如下:https://github.com/Snoopy008/ChooseAlbumFile

    结语##

    由于时间有限,我还是将PhotoKit的内容放在下一篇。敬请期待。。。。

    相关文章

      网友评论

      • UILabelkell:请教一下 ,获取视频 得到一个地址 能播放 怎么把视频 转成nsdata 呀
      • _码奴:怎么确定获取到的资源是照片还是视频,我需要把照片和视频分开处理,帖子中没有体现啊,网上都市说如何获取,获取到的都是url和缩略照片,但是我还是不知道哪个是视频,哪个是照片啊
        _码奴:@Snoopy008 我要是获取完资源后,在界面中做区分,上传服务器前做不同的处理。
        Snoopy008:@_码奴 根据[group setAssetsFilter:[ALAssetsFilter allPhotos]] 设置你要的是图片还是视频
      • 大Z哥:能不能获取图片/视频的真实名称?而不是这种:Printing description of asset->_filename:
        IMG_0006.MP4
        大Z哥:@Snoopy008 不是啊,我用暴风魔镜看相册的视频就显示真实的名字
        Snoopy008:@zerver 这就是真实名啊
      • JohnQ:博主,我想请问一下对于这个如何去获取这个相册中所有图片和视频的地址呢?我用NSURL *url = [[asset defaultRepresentation] url];这个方法获取到的图片地址为assets-library://asset/asset.JPG?id=BF5EF399-F7A5-4738-92C3-C5F6B71A33AC&ext=JPG这样的,这样的话我没办法放到图片的imagewithContentFile的方法中,虽然可以通过变相的去解决,可是我在预览视频的时候就会出现问题了,没有完整的正确地址根本没办法播放预览视频啊
        JohnQ:@somesOne 解决了 我用AVPlay控件播放视频的 地址也是取得asset地址 可以播放
        somesOne:你解决了没有,我也碰见这个问题了
      • Real_young:有可以获取多张图的吗?
        Snoopy008:@real_young 文章下方有链接
        Real_young:@Snoopy008 有Demo吗
        Snoopy008:可以的
      • Archer_bling:作者您好,这个框架的取相册分组就算用ALAssetsGroupAll好像也取不全,比如相册的个人收藏的相册就取不到,这个问题有办法解决吗?
        Snoopy008:@烬破军 可能是框架的问题。
        Archer_bling:@Snoopy008 但是个人收藏也是一个相簿,用现在的photokit是可以出来的,但是assetsLibrary出不来
        Snoopy008:@烬破军 这个你可以看一下,个人收藏里的相片都是其他相册的,如果其他相册的相片删掉,个人收藏里的也会删除。个人收藏里的相片其实是虚拟出来的,指向都是同一个相片。
      • kkkore:作者你好,看了你的文章帮助很大。 :+1:
        我有一个问题,就是你是如何去知道该如何使用,是通过看书?阅读API?翻阅别人的文章?我想了解的是,你是如何去获取这些知识的。可能很白痴的问题,不吝赐教。
        Snoopy008:@kkkore iOS的东西太多,最好是项目遇到或者自己特别感兴趣可以研究一下。最好先看一下官方API,然后翻墙找点资料。
      • Jack_lin:现在都得用photos了吧
        feng_dev:@Snoopy008 好像官方说 截止 ios9 不能用
        Snoopy008:@Jack_lin 目前大部分还是用Asset,photoKit的第三方比较少。
      • 林易析:很实用,一起加油

      本文标题:iOS 获取系统相册内视频与图片(AssetsLibrary篇)

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