iOS DeveloperiOS程序猿程序员

PhotoKit制作相册或选择器(二):相册分类

2016-09-12  本文已影响277人  Alan_Sim

前言

上一篇我们大概了解Photos框架的基本知识,这一篇我们就开始动手,造一款高仿UIImagePickerController的五菱专属轮子。

相册分类

效果图

先来看一波效果,提提精神!


相册分类.gif

开始

UIImagePickerController继承的是UINavigationController,目的是点击相册分类可以Push进入照片界面,所以需要导航控制器作为基础。

@interface ASImagePickerController : UINavigationController

导航控制器的根控制器就是相册分类视图控制器(ASAlbumListController)

- (instancetype)init
{
    self = [super initWithRootViewController:[[ASAlbumListController alloc] init]];
    if (self) {
        
    }
    return self;
}

相册分类的视图控制器可以直接继承UITableViewController,这里小编就用UIViewController了。下一步导入Photos框架,这可是核心呀。

@import Photos;

@interface ASAlbumListController ()<UITableViewDataSource, UITableViewDelegate, PHPhotoLibraryChangeObserver>
//表视图,显示相册分类
@property (nonatomic, strong) UITableView *tableView;
//储存相册分类,包含allPhotos,SmartAlbum,Album
@property (nonatomic, strong) NSArray *sectionFetchResults;
//储存相册分类名
@property (nonatomic, strong) NSArray *sectionLocalizedTitles;

@end

配置数据源

#pragma mark - setup data
- (void)setupData {

    PHFetchOptions *photosOptions = [[PHFetchOptions alloc] init];
    //图片配置设置排序规则
    photosOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
    
    //获取所有图片资源
    PHFetchResult *allPhotos = [PHAsset fetchAssetsWithOptions:photosOptions];
    
    //获取智能相册
    PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
    
    //获取用户自定义相册
    PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
    self.sectionFetchResults = @[allPhotos, smartAlbums, topLevelUserCollections];
    self.sectionLocalizedTitles = @[@"", NSLocalizedString(@"Smart Albums", @""), NSLocalizedString(@"Albums", @"")];

}

相册缩略图预览(自定义Cell)

为了节省代码量,尽可能使用系统自带的Cell样式。UITableViewCellStyleDefault这个默认样式只有图片不符合需求,所以我就在此基础上做了些延伸

@interface ASAlbumCustomCell ()

@property (strong, nonatomic) UIImageView *frontImageView;

@property (strong, nonatomic) UIImageView *middleImageView;

@property (strong, nonatomic) UIImageView *lastImageView;

@end

这里得用懒加载,图片不够的话,对应的UIImageView也不必加载了,最多显示三张(这里偷懒了,设frame做了)
要注意的是视图的层级关系,要有一层一层的效果

#pragma mark - lazy load
- (UIImageView *)frontImageView {
    if (!_frontImageView) {
        _frontImageView = [[UIImageView alloc] initWithFrame:CGRectMake(8, 10, 69, 69)];
        _frontImageView.contentMode = UIViewContentModeScaleAspectFill;
        _frontImageView.clipsToBounds = YES;
        [self.contentView addSubview:_frontImageView];
    }
    return _frontImageView;
}

- (UIImageView *)middleImageView {
    if (!_middleImageView) {
        _middleImageView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 8, 65, 65)];
        _middleImageView.contentMode = UIViewContentModeScaleAspectFill;
        _middleImageView.clipsToBounds = YES;
        [self.contentView insertSubview:_middleImageView belowSubview:self.frontImageView];
    }
    return _middleImageView;
}

- (UIImageView *)lastImageView {
    if (!_lastImageView) {
        _lastImageView = [[UIImageView alloc] initWithFrame:CGRectMake(12, 6, 61, 61)];
        _lastImageView.contentMode = UIViewContentModeScaleAspectFill;
        _lastImageView.clipsToBounds = YES;
        [self.contentView insertSubview:_lastImageView belowSubview:self.middleImageView];
    }
    return _lastImageView;
}

现在需要用这三个叠加的UIImageView来替代UITableViewCell中的UIImageView,通过填充,制造了“假象”

#pragma mark - customPageViews

- (void)customPageViews {
    
    self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    CGSize itemSize = CGSizeMake(65.f, 65.f);
    
    UIGraphicsBeginImageContext(itemSize);
    
    CGRect imageRect = CGRectMake(0.f, 0.f, itemSize.width, itemSize.height);
    
    [self.imageView.image drawInRect:imageRect];
    
    self.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
    
    UIGraphicsEndImageContext();
}

配置数据,赋值UIImageView

#pragma mark - setupData

- (void)setupData {
    for (int i = 0; i < self.thumbImages.count; i++) {
        switch (i) {
            case 0:{
                self.frontImageView.image = self.thumbImages[i];
                break;
            }
            case 1:{
                self.middleImageView.image = self.thumbImages[i];
                break;
            }
            case 2:{
                self.lastImageView.image = self.thumbImages[i];
                break;
            }
            default:
                break;
        }
    }
}

数组类型属性thumbImages在赋值后需要在ImageView上生效,所以需要写一下Setter方法

- (void)setThumbImages:(NSArray *)thumbImages {
    _thumbImages = thumbImages;
    [self setupData];
}

UITableView的数据源代理

static NSString * const AllPhotosReuseIdentifier = @"AS_AllPhotosCell";
static NSString * const CollectionCellReuseIdentifier = @"AS_CollectionCell";
static const float ListRowHeight = 89.f;

#pragma mark - system delegate
#pragma mark -- UITableViewDataSource
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    ASAlbumCustomCell *cell = nil;
    PHFetchResult *fetchResult = nil;
    if (indexPath.section == 0) {
        cell = [tableView dequeueReusableCellWithIdentifier:AllPhotosReuseIdentifier forIndexPath:indexPath];
        fetchResult = self.sectionFetchResults[indexPath.section];
        
        cell.textLabel.text = NSLocalizedString(@"All Photos", @"");
        
    } else {
        NSArray *collections = self.sectionFetchResults[indexPath.section];
        if (!collections || collections.count <= indexPath.row) return nil;
        PHCollection *collection = collections[indexPath.row];
        cell = [tableView dequeueReusableCellWithIdentifier:collection.localIdentifier];
        if (!cell) {
            cell = [[ASAlbumCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:collection.localIdentifier];
        }
        if ([collection isKindOfClass:[PHAssetCollection class]]) {
            fetchResult = [PHAsset fetchAssetsInAssetCollection:(PHAssetCollection *)collection options:nil];
            cell.textLabel.text = collection.localizedTitle;
        }
        //相册名
    }

    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    
    cell.detailTextLabel.text = [NSString stringWithFormat:@"%zi", fetchResult.count];
    //获取当前相册分类下的前三张图片
    __block NSInteger fetchImageIndex = 0;
    NSMutableArray *thumbsImages = [NSMutableArray arrayWithCapacity:3];
    for (NSInteger i = 0; i < MIN(fetchResult.count, 3); i++) {
        fetchImageIndex++;
        [[PHCachingImageManager defaultManager] requestImageForAsset:fetchResult[i] targetSize:CGSizeMake(69, 69) contentMode:PHImageContentModeAspectFit options:nil resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
            if (result) [thumbsImages addObject:result];
            if (--fetchImageIndex == 0) {
                cell.thumbImages = thumbsImages;
            }
        }];
    }
    
    return cell;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSInteger numberOfRows = 0;
    //第一个section就allPhotos一行
    if (section == 0) {
        numberOfRows = 1;
    } else {
        PHFetchResult *fetchResult = self.sectionFetchResults[section];
        numberOfRows = fetchResult.count;
    }
    
    return numberOfRows;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.sectionFetchResults.count;
}

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
    return self.sectionLocalizedTitles && self.sectionLocalizedTitles.count > section ? self.sectionLocalizedTitles[section] : @"";
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return ListRowHeight;
}

观察相册资源变化

Photos为开发者提供了PHPhotoLibraryChangeObserver,实现对相册资源变化的检测。
首先,先注册观察者

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];    
    //注册观察相册变化的观察者
    [[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
}

其次,添加协议

@interface ASAlbumListController ()<PHPhotoLibraryChangeObserver>

图片资源一旦有变化就会调用photoLibraryDidChange:,所以我们需要实现此方法,对相册变化做出反应

#pragma mark - PHPhotoLibraryChangeObserver

- (void)photoLibraryDidChange:(PHChange *)changeInstance {
    //观察者,在后台队列执行,所以刷新界面需要在主队列中
    dispatch_async(dispatch_get_main_queue(), ^{
        //深拷贝,备份比较
        NSMutableArray *updatedSectionFetchResults = [self.sectionFetchResults mutableCopy];
        __block BOOL reloadRequired = NO;
        
        [self.sectionFetchResults enumerateObjectsUsingBlock:^(PHFetchResult *collectionsFetchResult, NSUInteger index, BOOL *stop) {
            //根据原先的相片集的数据创建变化对象
            PHFetchResultChangeDetails *changeDetails = [changeInstance changeDetailsForFetchResult:collectionsFetchResult];
            //判断变化对象是否为空,不为空则代表有相册有变化
            if (changeDetails != nil) {
                //变化后的数据替换变化前的数据
                [updatedSectionFetchResults replaceObjectAtIndex:index withObject:[changeDetails fetchResultAfterChanges]];
                reloadRequired = YES;
            }
        }];
        
        if (reloadRequired) {
            //刷新数据
            self.sectionFetchResults = updatedSectionFetchResults;
            [self.tableView reloadData];
        }
        
    });
}

最后,销毁观察者

- (void)dealloc {
    //销毁观察相册变化的观察者
    [[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:self];
}

是不是很简单
现在可以运行起来,看一下效果咯~
如果有问题可以看一下小编提供的Demo

结束语

轮毂雏形出来了哟。这篇都是比较基础的用法,所以代码占了主要部分,里面就穿插了一点小编的思路。

下一篇,对应相册分类下的图片显示(包含了API中提到的缓存预加载策略)以及多选功能,先来瞄一眼效果~

照片展示.gif

ASImagePicker也在持续更新中...
https://github.com/alanshen0118/ASImagePicker

文章中有任何错误希望读者能积极指出,我会及时更正。
如果喜欢,请持续关注,顺便点个喜欢噢👇👇👇帮五菱加加油~@_@

Thanks!!!

上一篇下一篇

猜你喜欢

热点阅读