Photos.framework是iOS8后蘋果推出的一套替代AssetsLibrary.framework獲取相冊資源的原生庫般渡,至于AL庫锥惋,歡迎大家給博文iOS開發(fā)------簡單實現(xiàn)圖片多選功能(AssetsLibrary.framework篇)提出寶貴的意見。
樓主大部分都是查看官方開發(fā)文檔進行探索的(當然,實在不明白了也會請求google 的 0.0 )网棍。這里就說一下個人的看法吧师骗,相比AL庫历等,Photos的開發(fā)文檔顯然更像是目前我們接觸的ObjC語言(如果不信,可以對比一下AL庫和Photos庫的開發(fā)文檔)辟癌。初次接觸這個庫的時候可能會感覺比較亂寒屯,畢竟類的數(shù)量比AL庫多了好多,但在熟悉大體邏輯之后黍少,就會發(fā)現(xiàn)它的分工比AL更加明確寡夹,并且使用起來要比AL靈活的多。
提醒一下仍侥,要使用相冊資源庫的時候要出,為了適配一下將來的iOS10,不要忘記在info.plist文件中加入NSPhotoLibraryUsageDescription
這個描述字段啊农渊,更多的權(quán)限坑請關(guān)注一下博文 iOS開發(fā)------iOS 10 由于權(quán)限問題導(dǎo)致崩潰的那些坑
2018-05-18 更新
針對OC版本進行了重構(gòu)以及功能的擴充 OC傳送門
RITLPhotosViewController *photoController = RITLPhotosViewController.photosViewController;
photoController.configuration.maxCount = 5;//最大的選擇數(shù)目
photoController.configuration.containVideo = false;//選擇類型患蹂,目前只選擇圖片不選擇視頻
photoController.photo_delegate = self;
photoController.thumbnailSize = self.assetSize;//縮略圖的尺寸
[self presentViewController:photoController animated:true completion:^{}];
2017-01-22 更新
Swift - 全代碼:https://github.com/RITL/Swift-RITLImagePickerDemo
Swift-Demo 重寫完不久,可能Swift的編程思想還不是領(lǐng)略的很好砸紊,但樓主會繼續(xù)努力传于。
Swift用法:
//獲得控制器
let viewController : RITLPhotoNavigationViewController = RITLPhotoNavigationViewController()
//設(shè)置viewModel屬性
let viewModel = viewController.viewModel
// 獲得圖片
viewModel.completeUsingImage = {(images) in
}
// 獲得資源的data數(shù)據(jù)
viewModel.completeUsingData = {(datas) in
//coding for data ex: uploading..
print("data = \(datas)")
}
self.present(viewController, animated: true) {}
話說的有點多了,下面就談?wù)剛€人對Photos的理解醉顽,這里只記錄一下Photos.framework中類的使用與理解沼溜,真正的實現(xiàn)多選功能請前去上面的鏈接下載demo查看,多謝指正:
類邏輯
研究一個庫或者框架,總體邏輯一定是要縷清的吧彪,下面就是個人對photos的理解拦耐,有點多涂乌,分類一下吧:
資源類
- PHPhotoLibrary 是一個資源庫会傲。能夠獲取相冊權(quán)限以及對相冊的操作序矩,與AL不同秧均,它不能獲取資源對象哦.
- PHFetchResult 是一個結(jié)果集锉罐,一個泛型類能耻。通過方法獲取到的相冊或者資源組就是被封裝成該類返回.
- PHAssetCollection 是一個資源集合對象赏枚。其實它就是一個相冊的概念,可通過
類方法
獲得想要的相冊集合晓猛,繼承自PHCollection. - PHCollectionList 是一個資源集合列表對象饿幅。剛接觸時以為它是存放PHCollection對象的集合,后來才知道戒职,如果想要通過地點以及時間分組的話栗恩,請使用這個類替代PHAssetCollection吧,用法與PHAssetCollection類似帕涌,同樣是繼承自PHCollection.
- PHAsset 是一個獨立的資源對象摄凡。可以通過
類方法
對PHCollection對象進行遍歷蚓曼,獲得存放Asset對象的結(jié)果集亲澡,可以直接獲得資源的規(guī)格數(shù)據(jù),若想獲得圖片以及原圖等資源纫版,需要配合PHImageManager對象床绪,繼承自PHObject.
工具類
- PHFetchOptions 一個遍歷配置類。一般情況下其弊,當存在遍歷方法的時候就存在這個類型的參數(shù)癞己,里面含有謂詞、遍歷順序等屬性梭伐,可以通過設(shè)置這些屬性痹雅,完成不同的遍歷.
- PHImageManager 是一個負責渲染資源的類。比如獲得PHAsset對象的原圖等操作需要使用該類.
- PHCachingImageManager 繼承自PHImageManager糊识,可以對請求的資源對象進行緩存绩社,這樣再次獲取時就不需要重新渲染,在加快獲取速度的同時也降低了CPU的壓力赂苗,這里最好對緩存的PHImageRequestID進行一下記錄愉耙,防止同一資源被無限緩存的尷尬.
- PHImageRequestOptions 是一個資源請求的配置類。通常在使用PHImageManager對某個資源進行請求時都會存在此類型的參數(shù)拌滋,可以在請求資源時對該對象進行設(shè)置朴沿,獲得想要的結(jié)果,比如原圖..
請求類
- 請求類不能獨立使用,要想發(fā)揮作用赌渣,需要與PHPhotoLibrary對象配合使用.
- PHAssetCollectionChangeRequest 集合變化請求類魏铅,負責對PHAssetCollection對象的操作
- PHCollectionListChangeRequest 集合變化請求類,負責PHCollectionList對象的操作
- PHAssetChangeRequest 資源變化請求類坚芜,負責PHAsset對象的操作
類庫中的類及其屬性方法:
這里提到的都是代碼中用到的屬性和方法沦零,如果只是為了多圖選擇,那么以下的方法應(yīng)該是夠用的货岭,不夠的話可以Command+單擊進入開發(fā)文檔查看即可。
PHPhotoLibrary_照片庫
基礎(chǔ)方法
我覺得下面的方法應(yīng)該都懂疾渴,畢竟每個涉及到權(quán)限的庫都會存在下面三個方法的.
//獲得單例對象
+ (PHPhotoLibrary *)sharedPhotoLibrary;
//獲得相冊權(quán)限
+ (PHAuthorizationStatus)authorizationStatus;
//請求權(quán)限
+ (void)requestAuthorization:(void(^)(PHAuthorizationStatus status))handler;
操作相冊
之前說請求類不能獨自使用千贯,需要配合PHPhotoLibrary對象,為什么這么說呢搞坝,是因為在使用請求類的時候必須使用下面兩個方法其中之一搔谴,下面是開發(fā)文檔的一句話:
/*表示請求只能通過下面兩種方法的block進行創(chuàng)建和使用,所有的ChangeRequest類上面都會存在這句話桩撮,當然類名肯定不一樣的.*/
PHAssetCollectionChangeRequest can only be created or used within a -[PHPhotoLibrary performChanges:]
or -[PHPhotoLibrary performChangesAndWait:] block.
//異步執(zhí)行change的變化請求
- (void)performChanges:(dispatch_block_t)changeBlock completionHandler:(nullable void(^)(BOOL success, NSError *__nullable error))completionHandler;
//同步執(zhí)行change的請求變化
- (BOOL)performChangesAndWait:(dispatch_block_t)changeBlock error:(NSError *__autoreleasing *)error;
其實我感覺只看上面的兩個方法感覺會比較抽象敦第,那么就拿出Demo中的兩段小源碼舉個例子,相信這樣就比較好理解了.
新建相冊
///新建一個名字叫做title的相冊
-(void)addCustomGroupWithTitle:(NSString *)title
completionHandler:(void (^)(void))successBlock
failture:(void (^)(NSString * _Nonnull))failtureBlock
{
[self.photoLibaray performChanges:^{
//創(chuàng)建一個創(chuàng)建相冊的請求
[PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title];
}completionHandler:^(BOOL success, NSError * _Nullable error) {
if (success == true)//成功
{
successBlock();return ;
}
//失敗
failtureBlock(error.localizedDescription);
}];
}
向相冊里面添加圖片
///向collection中添加圖片
-(void)addCustomAsset:(UIImage *)image
collection:(PHAssetCollection *)collection
completionHandler:(void (^)(void))successBlock
failture:(void (^)(NSString * _Nonnull))failtureBlock
{
//執(zhí)行變化請求
[self.photoLibaray performChanges:^{
//如果相冊允許操作
if([collection canPerformEditOperation:PHCollectionEditOperationAddContent])
{
//創(chuàng)建資源請求對象
PHAssetChangeRequest * assetChangeRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:image];
//創(chuàng)建相冊請求對象
PHAssetCollectionChangeRequest * groupChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:collection];
//向相冊中添加資源
[groupChangeRequest addAssets:@[assetChangeRequest.placeholderForCreatedAsset]];
}
}completionHandler:^(BOOL success, NSError * _Nullable error) {
if (success == true)//成功
{
successBlock();return;
}
//失敗
failtureBlock(error.localizedDescription);
}];
}
//這里不止能夠通過圖片對象創(chuàng)建店量,還存在如下兩種創(chuàng)建方法
+ (nullable instancetype)creationRequestForAssetFromImageAtFileURL:(NSURL *)fileURL;//通過圖片所在的路徑url進行創(chuàng)建
+ (nullable instancetype)creationRequestForAssetFromVideoAtFileURL:(NSURL *)fileURL;//通過視頻所在的路徑url進行創(chuàng)建
PHAssetCollection_資源集合對象
- 常用屬性
//組的標題芜果,比如Camera Roll(膠卷相冊)
@property (nonatomic, strong, readonly, nullable) NSString *localizedTitle;
//資源組的類型,比如是智能相冊融师,普通相冊還是外界創(chuàng)建的相冊
PHAssetCollectionType assetCollectionType;
typedef NS_ENUM(NSInteger, PHAssetCollectionType) {
PHAssetCollectionTypeAlbum = 1, //傳統(tǒng)相冊
PHAssetCollectionTypeSmartAlbum = 2, //智能相冊
PHAssetCollectionTypeMoment = 3, //自定義創(chuàng)建的相冊
} NS_ENUM_AVAILABLE_IOS(8_0);
//具體的子類型右钾,比如是智能相冊的自拍還是喜愛等,這個枚舉類太多旱爆,就不進行粘貼了.
PHAssetCollectionSubtype assetCollectionSubtype;
//資源組中資源的大約數(shù)量舀射,不一定準,如果想要確切的怀伦,獲得PHFetchResult對象取count即可
NSUInteger estimatedAssetCount;
//最早的一張圖片存在相冊的時間
NSDate *startDate;
//最近的一張圖片存在相冊的時間
NSDate *endDate;
- 獲得具體資源的方法脆烟,基本都是通過類方法進行獲取,這樣就降低了PhotosLibrary對象復(fù)雜度房待。
//判斷是否能夠進行編輯邢羔,如果是進行修改請求,最好通過這個方法來判斷是下
- (BOOL)canPerformEditOperation:(PHCollectionEditOperation)anOperation;
//獲得智能分組吴攒,比如膠卷相冊张抄,最近添加,自拍等
PHFetchResult * smartGroups = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum
subtype:PHAssetCollectionSubtypeAlbumRegular
options:nil];
//獲得我們自定義創(chuàng)建的相冊組洼怔,比如有QQ的手機應(yīng)該都會有QQ這個相冊署惯,那么通過該方法就可以獲取的到
+ (PHFetchResult<PHCollection *> *)fetchTopLevelUserCollectionsWithOptions:(nullable PHFetchOptions *)options;
如果PHFetchResult覺得用起來不是很爽的話,可以將其包裝成數(shù)組來進行下一步的操作镣隶,Demo中就是將其打包成數(shù)組來進行操作的:
@implementation PHFetchResult (NSArray)
//將PHFetchResult對象轉(zhuǎn)成NSArray對象
-(void)transToArrayComplete:(void (^)(NSArray<PHAssetCollection *> * _Nonnull, PHFetchResult * _Nonnull))arrayObject
{
__weak typeof(self) weakSelf = self;
NSMutableArray * array = [NSMutableArray arrayWithCapacity:0];
if (self.count == 0)
{
arrayObject([array mutableCopy],weakSelf);
array = nil;
return;
}
[self enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[array addObject:obj];
//如果遍歷完畢极谊,進行回調(diào)
if (idx == self.count - 1)
{
arrayObject([array mutableCopy],weakSelf);
}
}];
}
@end
PHAsset_資源對象
- 常用屬性
//資源媒體的類型
PHAssetMediaType mediaType;
typedef NS_ENUM(NSInteger, PHAssetMediaType) {
PHAssetMediaTypeUnknown = 0, //未知類型
PHAssetMediaTypeImage = 1, //圖片類型
PHAssetMediaTypeVideo = 2, //視頻類型
PHAssetMediaTypeAudio = 3, //音頻類型
} NS_ENUM_AVAILABLE_IOS(8_0);
//資源美圖的子類型诡右,比如如果資源是圖片,那么它是全景還是HDR,如果是iOS9,還能知道他是屏幕截圖還是live圖片轻猖,枚舉有點多帆吻,也不再次粘貼了.
PHAssetMediaSubtype mediaSubtypes;
//資源的像素寬
NSUInteger pixelWidth;
//資源的像素高
NSUInteger pixelHeight;
//資源的創(chuàng)建日期
NSDate *creationDate;
//資源的最近一次修改的時間
NSDate *modificationDate;
//資源拍攝的地點
CLLocation *location;
//如果是音頻或者視頻,它的持續(xù)時間
NSTimeInterval duration;
//它是否被隱藏
BOOL hidden;
//它是否是喜愛的
BOOL favorite;
- 常用搭配使用方法
//請求圖片咙边,將想要獲取Image實體的資源猜煮,裁剪的大小以及方式進行獲取圖片
//如果想要原圖,設(shè)置PHImageRequestOptions對象的deliveryMode屬性為PHImageRequestOptionsDeliveryModeHighQualityFormat即可
[[PHImageManager defaultManager]requestImageForAsset:asset
targetSize:size
contentMode:PHImageContentModeAspectFill
options:option
resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
//通過block回傳圖片败许,并將部分信息存在于info字典中王带,并且該方法的返回值可以在info字典中找到
}];
//請求數(shù)據(jù),獲取資源的Data對象市殷,可以用來計算資源的大小愕撰。
//這樣可以避免UIImage->Data導(dǎo)致內(nèi)存以及CPU瞬間激增
[[PHImageManager defaultManager]requestImageDataForAsset:asset
options:option
resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
//在block里面獲取圖片的各種信息
}];
//這里最好存在一個標位置或者其他方法標志一下,避免每次都要緩存導(dǎo)致的卡頓以及CPU卡死
[((PHCachingImageManager *)[PHCachingImageManager defaultManager])startCachingImagesForAssets:@[copy_self]
targetSize:newSize
contentMode:PHImageContentModeAspectFill
options:nil];
PHFetchResult_結(jié)果集合
表示乍一看醋寝,是不是和數(shù)組很相似呀.
- 屬性
//當前集合的數(shù)量
NSUInteger count;
//獲得index位置的對象
- (ObjectType)objectAtIndex:(NSUInteger)index;
//是否包含對象
- (BOOL)containsObject:(ObjectType)anObject;
//對象的索引
- (NSUInteger)indexOfObject:(ObjectType)anObject;
//第一個對象
ObjectType firstObject;
//最后一個對象
ObjectType lastObject;
- 遍歷方法搞挣,應(yīng)該也是獲取集合內(nèi)部數(shù)據(jù)的唯一方法了.
/*使用Block進行枚舉遍歷,stop控制是否停止音羞,每次獲得數(shù)據(jù)都會執(zhí)行一次回調(diào)*/
- (void)enumerateObjectsUsingBlock:(void (^)(ObjectType obj, NSUInteger idx, BOOL *stop))block;
/*根據(jù)枚舉類型進行枚舉囱桨,比如正序還是倒序*/
- (void)enumerateObjectsWithOptions:(NSEnumerationOptions)opts usingBlock:(void (^)(ObjectType obj, NSUInteger idx, BOOL *stop))block;
/*枚舉特定區(qū)間并按照響應(yīng)枚舉類型進行遍歷*/
- (void)enumerateObjectsAtIndexes:(NSIndexSet *)s options:(NSEnumerationOptions)opts usingBlock:(void (^)(ObjectType obj, NSUInteger idx, BOOL *stop))block;
相冊發(fā)生變化
使用PHPhotoLibrary對象注冊觀察者,當然黄选,不要在dealloc或者特定的地方注銷觀察者啊蝇摸,與KVO相同.
//注冊觀察者
- (void)registerChangeObserver:(id<PHPhotoLibraryChangeObserver>)observer;
//注銷觀察者
- (void)unregisterChangeObserver:(id<PHPhotoLibraryChangeObserver>)observer;
PHPhotoLibraryChangeObserver
協(xié)議方法
//This callback is invoked on an arbitrary serial queue.
//If you need this to be handled on a specific queue,
//you should redispatch appropriately.
//這個回調(diào)是在一個隨意的線程中被喚起,如果需要在一個特定的線程中處理办陷,應(yīng)該在合適的地方重新喚起
//(翻譯可能不太準確貌夕,如果需要更新,一般情況需要在主線程民镜,這個應(yīng)該都懂得.)
- (void)photoLibraryDidChange:(PHChange *)changeInstance;
實例
具體用法如下:
首先需要查看是否發(fā)生了變化啡专,如果沒有變化,那么就返回nil制圈;如果發(fā)生了變化们童,就會返回響應(yīng)類型的對象:
//PHChange對象的兩個方法:
//獲取PHObject對象的變化詳情,其實PHAsset和PHCollection都是繼承自PHObject的
- (nullable PHObjectChangeDetails *)changeDetailsForObject:(PHObject *)object;
-
//獲取結(jié)果集的變化詳情鲸鹦,和上面一樣慧库,如果不存在變化,返回nil
- (nullable PHFetchResultChangeDetails *)changeDetailsForFetchResult:(PHFetchResult *)object;
下面記錄一下描述詳細變化的類吧:
PHObjectChangeDetails
//PHObject對象變化詳情對象
PHObjectChangeDetails.h
//變化之前的對象
@property (atomic, strong, readonly) __kindof PHObject *objectBeforeChanges;
//變化之后的對象馋嗜,
@property (atomic, strong, readonly, nullable) __kindof PHObject *objectAfterChanges;
//內(nèi)容是否發(fā)生了變化
@property (atomic, readonly) BOOL assetContentChanged;
//該對象是否已經(jīng)刪除
@property (atomic, readonly) BOOL objectWasDeleted;
@end
/*
1齐板、判斷一下是否被刪除了,如果被刪除了,那么請把數(shù)據(jù)源的該對象也刪除了吧甘磨,并重新reload一下當前的視圖.
2橡羞、如果沒有被刪除,就可以知道是否發(fā)生了變化济舆,那么卿泽,獲取變化后的內(nèi)容對象并將之前的內(nèi)容replace一下,刷新視圖即可
*/
PHFetchResultChangeDetails
//PHFetchResult對象發(fā)生變化的詳情類
PHFetchResultChangeDetails.h
//變化之前的結(jié)果集
@property (atomic, strong, readonly) PHFetchResult *fetchResultBeforeChanges;
//變化之后的結(jié)果集
@property (atomic, strong, readonly) PHFetchResult *fetchResultAfterChanges;
/*
這個變量很有意思:
如果為true,表示集合發(fā)生了增刪改,那么通過一下面的刪除滋觉、新增以及更新操作的響應(yīng)屬性或方法進行數(shù)據(jù)的修改即可签夭。
如果為false,則表示發(fā)生了大的改變,不在提供下面那些變化的詳情椎侠,只能使用fetchResultAfterChanges屬性對該屬性進行替換即可*/
@property (atomic, assign, readonly) BOOL hasIncrementalChanges;
//如果是刪除操作覆致,返回刪除的位置以及刪除的對象
@property (atomic, strong, readonly, nullable) NSIndexSet *removedIndexes;
@property (atomic, strong, readonly) NSArray<__kindof PHObject *> *removedObjects;
//如果是新增操作,返回新增的位置以及新增的對象
@property (atomic, strong, readonly, nullable) NSIndexSet *insertedIndexes;
@property (atomic, strong, readonly) NSArray<__kindof PHObject *> *insertedObjects;
//如果是更新操作肺蔚,返回更新的位置以及更新的對象
@property (atomic, strong, readonly, nullable) NSIndexSet *changedIndexes;
@property (atomic, strong, readonly) NSArray<__kindof PHObject *> *changedObjects;
/*
1、判斷一下hasIncrementalChanges值儡羔,如果為false,直接取fetchResultAfterChanges屬性進行值的替換
2宣羊、如果為true,根據(jù)下面的詳情數(shù)據(jù)進行相應(yīng)操作即可,當然汰蜘,使用全體替換的方法也是可以的仇冯,但是單個操作可以使用動畫哦
*/
吐槽
我要開始吐槽啦!其實畢業(yè)回學校的時候無聊族操,想練習一下Swift苛坚,研究過這個庫,大體的功能與博文Demo的功能差不多色难,但由于起名太隨便泼舱,隨手刪了,沒錯..刪了!(還真是感謝我隨手清理垃圾簍的習慣枷莉,呵呵....所以大家一定要不忘記備份呀娇昙,= = 再就是起名不要太隨便啊),這個Demo是趁工作的業(yè)余時間寫的笤妙,目的是加深對Photos庫理解的同時不辜負學校的那段時間冒掌,<font color=red>不太建議直接拿本文的Demo直接放入工程中使用哦,Demo的目的只是為了學習一下Photos庫蹲盘,盡管對Demo進行了一些內(nèi)存泄露的處理股毫,但每次還是會有大約1MB多的內(nèi)存多余占用,這個問題會在之后樓主不斷進步的過程中進行修復(fù)的</font>
記錄完畢3Q