iOS PhotoKit 教程
Demo,簡單實現了照片選擇器的功能
- PHAsset: 代表照片庫中的一個資源,跟 ALAsset 類似,通過 PHAsset 可以獲取和儲存資源
- PHFetchOptions: 獲取資源時的引數,可以傳 nil,即使用系統預設值
- PHAssetCollection: PHCollection 的子類,表示一個相簿或者一個時刻,或者是一個「智慧相簿(系統提供的特定的一系列相簿,例如:最近刪除,視訊列表,收藏等等,如下圖所示)
- PHFetchResult: 表示一系列的資源結果集合,也可以是相簿的集合,從?PHCollection 的類方法中獲得
- PHImageManager: 用於處理資源的載入,載入圖片的過程帶有快取處理,可以通過傳入一個 PHImageRequestOptions 控制資源的輸出尺寸等規格
- PHImageRequestOptions: 如上面所說,控制載入圖片時的一系列引數
常用的概念
PHCachingImageManager
提供檢索或生成預覽縮圖以及與Photos資源相關聯的全尺寸圖片或視訊資料的方法,這些圖片或視訊資料針對批量預載入大量資產進行了優化。
為了在使用許多資產時快速執行,快取影象管理器可以在後臺準備資產影象,以便在以後請求單個影象時消除延遲。例如,當您希望使用照片或視訊資源的縮圖填充集合檢視或類似的使用者介面時,請使用快取圖片管理器。
PHCachingImageManager類的許多關鍵功能由其超類PHImageManager定義。有關詳細資訊,請參閱PHImageManager。
要使用快取影象管理器:
- 建立PHCachingImageManager例項。 (此步驟使用共享的PHImageManager例項替換。)
使用PHAsset類方法獲取您感興趣的資產。 - 要為這些資產準備影象,請呼叫startCachingImages(for:targetSize:contentMode:options :)方法,使用目標大小,內容模式和計劃在稍後為每個單獨資產請求影象時使用的選項。
- 當您需要單個資產的影象時,請呼叫requestImage(for:targetSize:contentMode:options:resultHandler :)方法,並傳遞您在準備該資產時使用的相同引數。
- 如果您請求的影象在已準備好的影象中,則PHCachingImageManager物件立即返回該影象。否則,照片會根據需要準備影象,並將其快取以供以後使用。
PHFetchResult
從照片提取方法返回的資源或集合的有序列表。
當您在PHAsset,PHCollection,PHAssetCollection和PHCollectionList類中使用類方法來檢索物件時,Photos會在提取結果中提供生成的物件。您可以使用與NSArray類所使用的方法和約定相同的方式訪問獲取結果的內容。然而,與NSArray物件不同,PHFetchResult物件根據需要從Photos庫動態載入其內容,即使在處理大量結果時也能提供最佳效能。
提取結果提供對其內容的執行緒安全訪問。抓取後,抓取結果的計數值為常量,並且抓取結果中的所有物件保持相同的localIdentifier值。 (要獲取更新的內容以獲取,請使用共享的PHPhotoLibrary物件註冊變更觀察器。)
提取結果快取其內容,在最近訪問的索引周圍保留一批物件。由於批處理之外的物件不再快取,訪問這些物件會導致重新獲取這些物件。此過程可能會導致更改以前從這些物件讀取的值。
PHFetchOptions
一組影響過濾,排序和管理結果的選項,當您提取資源或集合物件時,Photos會返回。
在PHAsset,PHCollection,PHAssetCollection和PHCollectionList類上使用類方法來獲取資產或集合時,會生成包含所請求物件的PHFetchResult物件。 您指定的選項控制抓取結果的物件,包括這些物件在抓取結果中的排列方式,以及Google相簿應如何通知您的應用抓取結果的更改。
照片僅支援謂詞和sortDescriptors屬性的一組受限制的鍵。 可用鍵的集合取決於您用來獲取資源或集合的類。
核心程式碼
累了…
NSString *kXJYGridCollectionViewCell = @"XJYGridCollectionViewCell";
@interface XJYPhotoSelectorController ()<PHPhotoLibraryChangeObserver>
@property (nonatomic, strong) XJYCollectionViewFlowLayout *layout;
//抓取結果
@property (nonatomic, strong) PHFetchResult<PHAsset *> *fetchResult;
@property (nonatomic, strong) PHCachingImageManager *imageManager;
@property (nonatomic, strong) PHAssetCollection *assetCollection;
@property (nonatomic, assign) CGSize thumbnailSize;
@property (nonatomic, assign) CGRect previousPreheatRect;
@end
@implementation XJYPhotoSelectorController
#pragma mark Class Method
- (instancetype)init {
self = [self initWithCollectionViewLayout:self.layout];
return self;
}
- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout *)layout {
if (self = [super initWithCollectionViewLayout:self.layout]) {
}
return self;
}
#pragma mark View Life Cycle
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
self.previousPreheatRect = CGRectZero;
[self resetCachedAssets];
self.automaticallyAdjustsScrollViewInsets = YES;
self.collectionView.backgroundColor = [UIColor whiteColor];
#pragma mark PhotoKit
self.imageManager = [PHCachingImageManager defaultManager];
[[PHPhotoLibrary sharedPhotoLibrary] registerChangeObserver:self];
PHFetchOptions *allPhotosOptions = [[PHFetchOptions alloc] init];
allPhotosOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
self.fetchResult = [PHAsset fetchAssetsWithOptions:allPhotosOptions];
#pragma mark Register Cell
// Register cell classes
[self.collectionView registerClass:[XJYGridCollectionViewCell class] forCellWithReuseIdentifier:kXJYGridCollectionViewCell];
//Scroll to End
NSInteger section = [self.collectionView numberOfSections] - 1;
NSInteger item = [self.collectionView numberOfItemsInSection:section] - 1;
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:item inSection:section];
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:(UICollectionViewScrollPositionBottom) animated:YES];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self updateCachedAssets];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
[[PHPhotoLibrary sharedPhotoLibrary] unregisterChangeObserver:self];
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#define kScreen_Height [UIScreen mainScreen].bounds.size.height
#define kScreen_Width [UIScreen mainScreen].bounds.size.width
#pragma mark Getter
- (XJYCollectionViewFlowLayout *)layout {
if (!_layout) {
_layout = [[XJYCollectionViewFlowLayout alloc] init];
_layout.itemSize = CGSizeMake((kScreen_Width-20)/4, (kScreen_Width-20)/4);
_layout.minimumLineSpacing = 2.0;
_layout.minimumInteritemSpacing = 2.0;
_layout.scrollDirection = UICollectionViewScrollDirectionVertical;
_layout.sectionInset = UIEdgeInsetsMake(5, 5, 5, 5);
}
return _layout;
}
- (CGSize)thumbnailSize {
CGFloat scale = [UIScreen mainScreen].scale;
_thumbnailSize = CGSizeMake(scale * self.layout.itemSize.width, scale * self.layout.itemSize.height);
return _thumbnailSize;
}
#pragma mark <UICollectionViewDataSource>
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return self.fetchResult.count;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
XJYGridCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kXJYGridCollectionViewCell forIndexPath:indexPath];
//根據Index 獲取asset
PHAsset *asset = [self.fetchResult objectAtIndex:indexPath.item];
//設定cell representedAssetIdentifier
cell.representedAssetIdentifier = asset.localIdentifier;
//imageManager 請求image
[self.imageManager requestImageForAsset:asset targetSize:self.thumbnailSize contentMode:PHImageContentModeAspectFill options:nil resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
if ([cell.representedAssetIdentifier isEqualToString: asset.localIdentifier]) {
cell.image = result;
}
}];
return cell;
}