#前言 最近做的项目中遇到了自定义相册这个需求,于是就搜索到了Photos这个框架,说来惭愧。。这个是iOS8的东西,现在才用到。。由于网上的各种讲解也很多,我就只讲讲里面的遇到的坑和用法,当做笔记记录一下吧

先来看一下效果图


大概就是这样的,点击按钮获取系统相册资源,然后放到自定义的界面中,我们的项目底部需要加入预览的scroll,然后就是大图查看,图片回调。。具体的代码里面有详细注释。。

#好了,简单粗暴,直接上关键代码 ##获取相册权限

//相册权限判断
        PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
        if (status == PHAuthorizationStatusDenied)
        {
            //相册权限未开启
            NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
            // app名称
            NSString *app_Name = [infoDictionary objectForKey:@"CFBundleDisplayName"];
            
            [weakSelf SetAlertWithTitle:@"提醒" andMessage:[NSString stringWithFormat:@"请在iPhone的“设置->隐私->照片”开启%@访问你的手机相册",app_Name]];
            
        }
        else if(status == PHAuthorizationStatusNotDetermined)
        {
            //相册进行授权
            /* * * 第一次安装应用时直接进行这个判断进行授权 * * */
            [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status)
            {
                //授权后直接打开照片库
                if (status == PHAuthorizationStatusAuthorized)
                {
                    dispatch_async(dispatch_get_main_queue(), ^
                    {
                        [weakSelf pushViewController];
                    });
                    
                }
            }];
        }
        else if (status == PHAuthorizationStatusAuthorized)
        {
            [weakSelf pushViewController];
        }
复制代码

##然后就是获取系统相册资源了

-(void) getSystemPhotos
{
    // 获取所有资源的集合,并按资源的创建时间排序
    PHFetchOptions *options = [[PHFetchOptions alloc] init];
    options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
    
    dispatch_async(dispatch_get_global_queue(0,0), ^{
        // 获得相机胶卷的图片
        PHFetchResult<PHAssetCollection *> *collectionResult1 = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
        for (PHAssetCollection *collection in collectionResult1) {
            if (![collection.localizedTitle isEqualToString:@"Camera Roll"]) continue;
            [self searchAllImagesInCollection:collection];
            break;
        }
    });
}

- (void)searchAllImagesInCollection:(PHAssetCollection *)collection
{
    // 采取同步获取图片(只获得一次图片)
    PHImageRequestOptions *imageOptions = [[PHImageRequestOptions alloc] init];
    imageOptions.resizeMode = PHImageRequestOptionsResizeModeFast;
    imageOptions.synchronous = YES;
    imageOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
    
    PHVideoRequestOptions *videoOptions = [[PHVideoRequestOptions alloc] init];
    videoOptions.deliveryMode = PHVideoRequestOptionsDeliveryModeFastFormat;
    
    __weak typeof (self) weakSelf = self;
    
    // 遍历这个相册中的所有资源
    PHFetchResult<PHAsset *> *assetResult = [PHAsset fetchAssetsInAssetCollection:collection options:nil];
    
    for (PHAsset *asset in assetResult) {
        
        if (weakSelf.isPhotoModel) {
            // 过滤非图片
            if (asset.mediaType != PHAssetMediaTypeImage) continue;
            
            PhotoModel *photo = [[PhotoModel alloc] init];
            photo.asset = asset;
            [_imgViewArr addObject:photo];
        }
        else
        {
            // 过滤非视频
            if (asset.mediaType != PHAssetMediaTypeVideo) continue;
            
            PhotoModel *photo = [[PhotoModel alloc] init];
            photo.asset = asset;
            [_imgViewArr addObject:photo];
        }
    }
    
    dispatch_async(dispatch_get_main_queue(), ^{
        [weakSelf loadMainView];
    });
    
}
复制代码

这里有一个重点,就是获取到的asset资源一定不要尝试在这里使用requestImageForAsset方法去获得图片信息,特别是高清图,一到真机上几百张图片加载内存暴增导致闪退,就算使用裁剪图片或者PHImageRequestOptionsResizeModeFast模式,也会有一小段的空白时间,并不能进来后瞬间显示图片,所以此处我选择直接将asset保存进数组中,利用collctionview的重用机制,在自定义的cell里面再去使用requestImageForAsset加载,效果会好很多,自定义CollectionViewCell中的代码如下

-(void)loadPhotoData:(PhotoModel *)photo withTargetSize:(CGSize)target state:(BOOL)isPhotoModel
{
    __weak typeof (self) weakSelf = self;
    
    if (photo)
    {
        [[PHImageManager defaultManager] requestImageForAsset:photo.asset targetSize:CGSizeMake(200, 200) contentMode:PHImageContentModeAspectFit options:nil resultHandler:^(UIImage *result, NSDictionary *info){
            
            //修正图片方向
            UIImage *targetImg = [weakSelf fixOrientation:result];
            
            //缩小图片
            targetImg = [weakSelf reSizeImage:targetImg ForSize:target];
            
            weakSelf.PhotoImg.image = targetImg;
            photo.isPhotoModel = YES;
        }];
        
        if (!isPhotoModel) {
            
            _bottom.hidden = NO;
            
            PHImageManager *manager = [PHImageManager defaultManager];
            [manager requestAVAssetForVideo:photo.asset options:nil resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info)
             {
                 //CMTime转换
                 NSTimeInterval total = CMTimeGetSeconds(asset.duration);
                 
                 NSString *duration = [weakSelf stringWithTime:total];
                 
                 //回到主线程
                 dispatch_async(dispatch_get_main_queue(), ^{
                     weakSelf.VideoDuration.text = duration;
                     
                     photo.asset_video = asset;
                     photo.isPhotoModel = NO;
                 });
             }];
        }
    }
}
复制代码

这段代码中还加入了是否为图片模式的判断,如果是选择的视频,则这个cell中就将视频asset保存到对应的key中,之后再通过AVPlayer去播放这个资源

#结尾 首先感谢网上各位大神的资料讲解,我也看过好几份例子。。GitHub上那几个其实都有一点小问题(图片回调不全什么的,希望大神看到不要见怪)。。不能满足我们项目的需求,所以就自己撸了一个。。

目前我在使用Photos时遇到最大的一个坑就是上文中提到的内存暴增导致的闪退问题,一直尝试着在外部获取高清图片再保存进数组,后来无意间发现利用collctionview的重用就解决了这个问题,GIF中的图片回调什么的我就不一一列举了,代码里面我几乎都注释了,需要的可以去看看,觉得有用的话记得给个star吧~

PS:对了,我摄像头拍照录视频的功能还没加进来,如果有需要,时间充裕的话,我会尽量补上~

地址:GitHub

#更新 现已加入摄像头拍照录像功能、加入iOS9之后的collection长按拖动图片功能、优化图片选择器中的卡顿现象,去除在collectioncell中绘制图片代码,加快加载速度,以下代码已去除

//修正图片方向
UIImage *targetImg = [weakSelf fixOrientation:result];
            
//缩小图片
targetImg = [weakSelf reSizeImage:targetImg ForSize:target];
复制代码

真机测试效果更佳,大家用真机测试吧,代码都在GitHub中,注释很详细,大家可以去看,觉得有用记得点个star~