PhotoKit制作相冊(cè)或選擇器(三):照片展示

前言

上一篇里已經(jīng)將相冊(cè)分類列表界面完成了,乍一看是不是跟系統(tǒng)相冊(cè)很像呢。那這一篇我們就來把這個(gè)多選圖片選擇器的基本功能做完。

開始

數(shù)據(jù)源

照片展示界面的數(shù)據(jù)是由之前的相冊(cè)分類列表界面?zhèn)鱽淼?,所以切換至ASAlbumListController,實(shí)現(xiàn)UITableViewDelegate下的tableView:didSelectRowAtIndexPath:方法

#pragma mark -- UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    ASPhotoGridController *gridViewController = [[ASPhotoGridController alloc] init];
    
    PHFetchResult *fetchResult = self.sectionFetchResults[indexPath.section];
    
    if (indexPath.section == 0) {
        gridViewController.assetsFetchResults = fetchResult;
    } else {
        // 獲取選擇行的PHAssetCollection
        NSArray *sections = self.sectionFetchResults[indexPath.section];
        if (!sections || sections.count < indexPath.row) return;
        PHCollection *collection = sections[indexPath.row];
        if (![collection isKindOfClass:[PHAssetCollection class]]) return;
        
        PHAssetCollection *assetCollection = (PHAssetCollection *)collection;
        PHFetchResult *assetsFetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:nil];
        
        gridViewController.assetsFetchResults = assetsFetchResult;
    }
    
    [self.navigationController pushViewController:gridViewController animated:YES];
}
回到照片展示界面

毋庸置疑照片展示界面的主體就是一個(gè)UICollectionView,并且使用流布局排布。首先我們先自定義UICollectionViewCell來展示圖片。
需要傳入圖片,以及資源重用標(biāo)識(shí)。資源重用標(biāo)識(shí)的作用是在請(qǐng)求圖片到圖片后匹配Cell
選中狀態(tài)添加了maskLayer,實(shí)現(xiàn)簡單蒙板效果,maskLayercontents里存放選中的效果圖片,可自定義。

@interface ASPhotoGridCell : UICollectionViewCell

@property (nonatomic, strong) UIImage *thumbnailImage;

@property (nonatomic, copy) NSString *representedAssetIdentifier;

@property (strong, nonatomic) UIImageView *imageView;

@property (strong, nonatomic) CALayer *maskLayer;

@end

@implementation ASPhotoGridCell

- (void)setSelected:(BOOL)selected {
    [super setSelected:selected];
    if (selected) {
        [self.contentView.layer addSublayer:self.maskLayer];
    } else {
        [self.maskLayer removeFromSuperlayer];
    }
}

- (void)setThumbnailImage:(UIImage *)thumbnailImage {
    _thumbnailImage = thumbnailImage;
    self.imageView.image = thumbnailImage;
}

- (UIImageView *)imageView {
    if (!_imageView) {
        _imageView = [[UIImageView alloc] initWithFrame:self.bounds];
        _imageView.contentMode = UIViewContentModeScaleAspectFill;
        _imageView.clipsToBounds = YES;
        _imageView.backgroundColor = [UIColor redColor];
        [self.contentView addSubview:_imageView];
    }
    return _imageView;
}

- (CALayer *)maskLayer {
    if (!_maskLayer) {
        _maskLayer = [CALayer layer];
        _maskLayer.frame = self.bounds;
        _maskLayer.contents = (id)[UIImage imageNamed:@"mask"].CGImage;
        _maskLayer.backgroundColor = [UIColor colorWithRed:1.f green:1.f blue:1.f alpha:.2f].CGColor;
    }
    return _maskLayer;
}

@end
同樣地

控制器可以使用UICollectionViewController,但這里小編就用UIViewController代替了,延展性高一些。
添加協(xié)議,定義常量

@interface ASPhotoGridController () <PHPhotoLibraryChangeObserver, UICollectionViewDataSource>

@property (strong, nonatomic) UICollectionView *collectionView;

@end

static NSString * const CellReuseIdentifier = @"Cell";
static CGSize AssetGridThumbnailSize;
懶加載初始化UICollectionView
#pragma mark - getters and setters
- (UICollectionView *)collectionView {
    if (!_collectionView) {
        UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
        layout.minimumInteritemSpacing = 1;
        layout.minimumLineSpacing = 1;
        CGFloat itemWidth = (CGRectGetWidth([UIScreen mainScreen].bounds) - 4 + 1) / 4;
        layout.itemSize = CGSizeMake(itemWidth, itemWidth);
        _collectionView = [[UICollectionView alloc] initWithFrame:[UIScreen mainScreen].bounds collectionViewLayout:layout];
        _collectionView.backgroundColor = [UIColor whiteColor];
//支持多選
        _collectionView.allowsMultipleSelection = YES;
        [_collectionView registerClass:[ASPhotoGridCell class] forCellWithReuseIdentifier:CellReuseIdentifier];
        _collectionView.dataSource = self;
    }
    return _collectionView;
}
界面布局

根據(jù)流布局獲取Cell的尺寸,初始化常量AssetGridThumbnailSize

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self customPageViews];
    
    CGFloat scale = [UIScreen mainScreen].scale;
    CGSize cellSize = ((UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout).itemSize;
    AssetGridThumbnailSize = CGSizeMake(cellSize.width * scale, cellSize.height * scale);
    
}

- (void)customPageViews {
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"select" style:UIBarButtonItemStylePlain target:self action:@selector(e__confirmImagePickerAction)];
    [self.view addSubview:self.collectionView];
}
實(shí)現(xiàn)UICollectionView數(shù)據(jù)源代理

其中的圖片數(shù)據(jù)是每次都請(qǐng)求的,非常耗資源,不是很合理,下一篇我們會(huì)細(xì)講緩存預(yù)加載策略。

#pragma mark -- UICollectionViewDataSource

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return self.assetsFetchResults.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    PHAsset *asset = self.assetsFetchResults[indexPath.item];
    
    ASPhotoGridCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellReuseIdentifier forIndexPath:indexPath];
    cell.representedAssetIdentifier = asset.localIdentifier;
    
    // 請(qǐng)求圖片
    [[PHImageManager defaultManager] requestImageForAsset:asset
                                 targetSize:AssetGridThumbnailSize
                                contentMode:PHImageContentModeDefault
                                    options:nil
                              resultHandler:^(UIImage *result, NSDictionary *info) {
                                  //判斷當(dāng)前cell的資源是否是當(dāng)前獲取的資源
                                  if ([cell.representedAssetIdentifier isEqualToString:asset.localIdentifier]) {
                                      cell.thumbnailImage = result;
                                  }
                              }];
    
    return cell;
}

到此,可以先運(yùn)行起來看看了~

接下來我們得完善下功能

監(jiān)測(cè)相冊(cè)資源變化
  • 注冊(cè)觀察者
//注冊(cè)觀察相冊(cè)變化的觀察者
[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
  • 圖片資源一旦有變化就會(huì)調(diào)用photoLibraryDidChange:,所以我們需要實(shí)現(xiàn)此方法,對(duì)相冊(cè)變化做出反應(yīng)
#pragma mark -- PHPhotoLibraryChangeObserver

- (void)photoLibraryDidChange:(PHChange *)changeInstance {
    // 檢測(cè)是否有資源變化
    PHFetchResultChangeDetails *collectionChanges = [changeInstance changeDetailsForFetchResult:self.assetsFetchResults];
    if (collectionChanges == nil) {
        return;
    }
    
    // 通知在后臺(tái)隊(duì)列,重定位到主隊(duì)列進(jìn)行界面更新
    dispatch_async(dispatch_get_main_queue(), ^{
        
        self.assetsFetchResults = [collectionChanges fetchResultAfterChanges];
        
        UICollectionView *collectionView = self.collectionView;
        
        if (![collectionChanges hasIncrementalChanges] || [collectionChanges hasMoves]) {
            [collectionView reloadData];
            
        } else {
            // 如果相冊(cè)有變化,collectionview動(dòng)畫增刪改
            [collectionView performBatchUpdates:^{
                NSIndexSet *removedIndexes = [collectionChanges removedIndexes];
                if ([removedIndexes count] > 0) {
                    [collectionView deleteItemsAtIndexPaths:[self indexPathsFromIndexes:removedIndexes section:0]];
                }
                
                NSIndexSet *insertedIndexes = [collectionChanges insertedIndexes];
                if ([insertedIndexes count] > 0) {
                    [collectionView insertItemsAtIndexPaths:[self indexPathsFromIndexes:insertedIndexes section:0]];
                }
                
                NSIndexSet *changedIndexes = [collectionChanges changedIndexes];
                if ([changedIndexes count] > 0) {
                    [collectionView reloadItemsAtIndexPaths:[self indexPathsFromIndexes:changedIndexes section:0]];
                }
            } completion:NULL];
        }
        
    });
}

#pragma mark - public method

- (NSArray *)indexPathsFromIndexes:(NSIndexSet *)indexSet section:(NSUInteger)section {
    NSMutableArray *indexPaths = [NSMutableArray arrayWithCapacity:indexSet.count];
    [indexSet enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) {
        [indexPaths addObject:[NSIndexPath indexPathForItem:idx inSection:section]];
    }];
    return indexPaths;
}
  • 銷毀觀察者
//銷毀觀察相冊(cè)變化的觀察者
    [[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:self];
最后

不能忘本喲,還得獲取所選的圖片資源,這里小編使用的是Delegate,大家也能選擇Block或者Notification
請(qǐng)求圖片資源時(shí)有兩種方法,一種直接獲取固定尺寸的UIImage,還有一種是獲取圖片數(shù)據(jù),這里獲取的是后者。

@protocol ASImagePickerControllerDelegate <NSObject>

@optional
- (void)as_didFinishPickingImageData:(NSArray *)imageDatas;

@end

//////////////////////////////////////////////////////////////////////////////

#pragma mark - event response(e__method)
- (void)e__confirmImagePickerAction {
    NSMutableArray *imageDatas = [NSMutableArray array];
//獲取已選狀態(tài)的Items的IndexPath
    NSArray *selectedAssets = [self assetsAtIndexPaths:[self.collectionView indexPathsForSelectedItems]];
    __block NSInteger requestCompletedIndex = 0;
    for (PHAsset *asset in selectedAssets) {
//請(qǐng)求圖片資源
        [[PHImageManager defaultManager] requestImageDataForAsset:asset options:nil resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
            if (imageData) [imageDatas addObject:imageData];
            requestCompletedIndex++;
            if (requestCompletedIndex > selectedAssets.count - 1) {
                if ([self.delegate respondsToSelector:@selector(as_didFinishPickingImageData:)]) {
                    [self.delegate as_didFinishPickingImageData:imageDatas];
                }
                [self.navigationController dismissViewControllerAnimated:YES completion:nil];
            }
        }];
    }
}

//根據(jù)indexPath獲取相應(yīng)的資源
- (NSArray *)assetsAtIndexPaths:(NSArray *)indexPaths {
    if (indexPaths.count == 0) { return nil; }
    
    NSMutableArray *assets = [NSMutableArray arrayWithCapacity:indexPaths.count];
    for (NSIndexPath *indexPath in indexPaths) {
        PHAsset *asset = self.assetsFetchResults[indexPath.item];
        [assets addObject:asset];
    }
    
    return assets;
}

在每個(gè)界面進(jìn)行delegate的傳遞,最終在彈出ASImagePickerController的控制器內(nèi)實(shí)現(xiàn)代理以供顯示


@interface ViewController ()<ASImagePickerControllerDelegate, UINavigationControllerDelegate>

- (IBAction)chooseAssets:(id)sender {
    ASImagePickerController *imagePicker = [[ASImagePickerController alloc] init];
    imagePicker.delegate = self;
    [self presentViewController:imagePicker animated:YES completion:nil];
}

- (void)as_didFinishPickingImageData:(NSArray *)imageDatas {
    //已選圖片的顯示
}

到現(xiàn)在為止,一個(gè)多選功能的圖片選擇器就基本完成了。代碼基本都貼出來了,可以和提供的Demo對(duì)照起來看。

效果圖
照片展示2.gif

結(jié)束語

輪轂終于出來了,但可能還有點(diǎn)不平整,滾起來不夠順吧,下一篇我們會(huì)來加工一下,講一下Photos API里提到的預(yù)加載策略,這個(gè)含金量高一些。有興趣的碼友們持續(xù)關(guān)注噢~

ASImagePicker也在持續(xù)更新中...
https://github.com/alanshen0118/ASImagePicker

文章中有任何錯(cuò)誤希望讀者能積極指出,我會(huì)及時(shí)更正。
如果喜歡,請(qǐng)持續(xù)關(guān)注,順便點(diǎn)個(gè)喜歡噢??????幫五菱加加油~@_@

Thanks!!!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,221評(píng)論 4 61
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,001評(píng)論 25 709
  • 在初秋的時(shí)光里, 尋著夢(mèng)的足跡走在現(xiàn)實(shí)的生活里, 我聽見花落的聲音,看見葉子的凋零。 伴著彷徨的心情, 向往著, ...
    釃藇閱讀 322評(píng)論 0 2
  • 1.化解我金錢關(guān)系障礙的途徑是 放下姿態(tài),越過圍欄,主動(dòng)去找她 2.當(dāng)我如圖所提示的,在現(xiàn)實(shí)中我可以做些什麼 主動(dòng)...
    秀ShirleyZ閱讀 278評(píng)論 0 0
  • 閱讀原文 在UIImage上面繪制內(nèi)容,步驟 得到圖片 UIImage* image=[UIImage image...
    學(xué)生陳希閱讀 400評(píng)論 0 0

友情鏈接更多精彩內(nèi)容