前言
上一篇我们大概了解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中提到的缓存预加载策略)以及多选功能,先来瞄一眼效果~
ASImagePicker也在持续更新中...
https://github.com/alanshen0118/ASImagePicker
文章中有任何错误希望读者能积极指出,我会及时更正。
如果喜欢,请持续关注,顺便点个喜欢噢👇👇👇帮五菱加加油~@_@
网友评论