iOS中獲取系統(tǒng)相冊中的圖片

一、獲取單張圖片

1、利用UIImagePickerController可以從系統(tǒng)自帶的App中獲得圖片
2、設(shè)置代理,遵守代理協(xié)議

@interface ViewController () <UIImagePickerControllerDelegate,UINavigationControllerDelegate>

3、實現(xiàn)代理方法didFinishPickingMediaWithInfo

二、獲取多張圖片

思路:
1、導(dǎo)入頭文件#import <Photos/Photos.h>
2、PHAsset:一個資源,比如一張圖片或者一段視頻
3、PHAssetCollection:一個相簿
4、PHImageManager 圖片管理者,是單例,發(fā)送請求才能從asset獲取圖片
5、PHImageRequestOptions圖片請求選項
6、注意:這個類是iOS8開始推廣,iOS9開始廢棄之前的方法
系統(tǒng)適配iOS8之前,用下面的這個庫里面的API

#import <AssetsLibrary/AssetsLibrary.h>

獲取資源

照片庫中有兩種資源可供獲?。篜HAsset和PHCollection,前者代表圖像或視頻對象,后者是前者的集合或者自身類型的集合。PHCollection是一個基類,有PHAssetCollection和PHCollectionList兩個子類,分別代表Photos里面的相冊和文件夾。PHCollectionList里面可嵌套PHAssetCollection和自身類型,還支持多重嵌套。獲取PHAsset以及PHAssetCollection的過程類似于CoreData,如下所示,只能通過類方法來返回PHFetchResult,遍歷返回結(jié)果來獲取需要的資源。

Paste_Image.png
Paste_Image.png

注意,PHAsset、PHAssetCollection和PHCollectionList都是輕量級的不可變對象,使用這些類時并沒有將其代表的圖像或視頻或者集合載入內(nèi)存中,要使用其代表的圖像或視頻,需要通過PHImageManager類來請求。

請求圖像

關(guān)于PHImageManager類,

- requestImageForAsset:targetSize:contentMode:options:resultHandler:

你不應(yīng)該生成該類的實例,而是應(yīng)該使用該類的提供的單例對象,該方法提供指定的尺寸的圖像,與ALAssetLibrary庫相比,沒有了方便的縮略圖提供,但是ALAssetLibrary庫提供的縮略圖往往尺寸太小而且質(zhì)量很低,用在TableView上還行。
該方法在默認情況下是異步執(zhí)行的,而且Photos庫可能會很多次執(zhí)行resultHandler塊,因為對于制定的尺寸,Photos可能會提供底質(zhì)量的圖像已供臨時顯示,隨后會將指定尺寸的圖像返回,如果指定尺寸的高質(zhì)量的圖像有緩存,那么直接提供高質(zhì)量的圖像,而這些行為,可以通過options參數(shù)來定制。

PHImageRequestOptions類用于定制請求,上面的方法返回指定尺寸的圖像,如果你僅僅指定必要的參數(shù)而沒有對options進行配置的話,返回的圖像尺寸將會是原始圖像的尺寸,或者你指定的尺寸很小,這時候會按照你的要求來返回接近改尺寸的圖像。
PHImageRequestOptions有以下幾個重要的屬性:

synchronous:指定請求是否同步執(zhí)行
resizeMode:對請求的圖像怎樣縮放。有三種選擇:none,不縮放;Fast,盡快的提供接近或者稍微大于要求的尺寸,Exact,精準(zhǔn)的提供要求的尺寸。
deliveryModel:圖像質(zhì)量,有三中值:Opportunistic,在速度與質(zhì)量中均衡,HighQualityFormat,不管花費多長的時間,提供高質(zhì)量的圖像;FastFormat,以最快的速度提供好的質(zhì)量。這個屬性只有在synchronous為true時有效。
normalizedCropRect:用于對原始尺寸的圖像進行裁剪,基于比例坐標(biāo),只在resizeMode為Exact時有效

resizeMode默認是None,這也造成了返回圖像尺寸與要求尺寸不符,這點需要注意。這個要返回一個指定尺寸的圖像要避免兩層陷阱:一定要指定options參數(shù),resizeMode不能為None。

除了必要的請求圖像或是視頻的功能外,PHImageManager添加了兩大功能:
1、緩存圖像,由于其之類PHCachingImageManager實現(xiàn),緩存效率和空間管理能滿足大部分場景的需求;
2、裁剪圖像,這個功能很久以前就有強烈的需求。

localldentifier vs URL

Photos框架推出時,和原來的照片庫AssetLibrary框架之間還有些交互,PHAsset類的+ fetchAssetWithALAssetURLs:options: 和PHAssetCollection類的+ fetchAssetCollectionsWithALAssetGroupURLs:options:可以利用原來的AssetsLibrary提供的URL進行轉(zhuǎn)化,而在iOS9 中,原來的照片框架AssertLibrary已經(jīng)被廢棄了,官方要淡化照片庫中URL的概念,改之使用一個標(biāo)識符唯一代表一個資源,Photos框架中的根類PHObject只有一個公開接口localIdentifier,AssetsLibrary框架中無論是Asset還是AssetGroup的URL也是唯一標(biāo)識符,而且同時還是動態(tài)變化的,每次啟動應(yīng)用后獲取的URL和上一次是不一樣的,而且AssetGroup有一個PersistentID與PHObject的localidentifier 類似,但獲取比較麻煩。
localIdentifier屬性帶來的最大好處就是PHObject類實現(xiàn)了NSCopying協(xié)議,可以直接使用localIdentifier屬性對PHObject以及子類對象進行對比是否同一個對象。

獲取指定類型相冊

通過PHAssetCollection的以下方法來獲取指定的相冊:

func fetchAssetCollectionsWithType(_ type: PHAssetCollectionType, subtype subtype: PHAssetCollectionSubtype, options options: PHFetchOptions?) -> PHFetchResult

這個方法需要至少指定兩個參數(shù):

enum PHAssetCollectionType : Int { 
case Album //從 iTunes 同步來的相冊,以及用戶在 Photos 中自己建立的相冊 
case SmartAlbum //經(jīng)由相機得來的相冊 
case Moment //Photos 為我們自動生成的時間分組的相冊}

enum PHAssetCollectionSubtype : Int { 
case AlbumRegular //用戶在 Photos 中創(chuàng)建的相冊,也就是我所謂的邏輯相冊 
case AlbumSyncedEvent //使用 iTunes 從 Photos 照片庫或者 iPhoto 照片庫同步過來的事件。然而,在iTunes 12 以及iOS 9.0 beta4上,選用該類型沒法獲取同步的事件相冊,而必須使用AlbumSyncedAlbum。 
case AlbumSyncedFaces //使用 iTunes 從 Photos 照片庫或者 iPhoto 照片庫同步的人物相冊。
 case AlbumSyncedAlbum //做了 AlbumSyncedEvent 應(yīng)該做的事 
case AlbumImported //從相機或是外部存儲導(dǎo)入的相冊,完全沒有這方面的使用經(jīng)驗,沒法驗證。 
case AlbumMyPhotoStream //用戶的 iCloud 照片流
 case AlbumCloudShared //用戶使用 iCloud 共享的相冊 
case SmartAlbumGeneric //文檔解釋為非特殊類型的相冊,主要包括從 iPhoto 同步過來的相冊。由于本人的 iPhoto 已被 Photos 替代,無法驗證。不過,在我的 iPad mini 上是無法獲取的,而下面類型的相冊,盡管沒有包含照片或視頻,但能夠獲取到。 case SmartAlbumPanoramas //相機拍攝的全景照片 
case SmartAlbumVideos //相機拍攝的視頻 
case SmartAlbumFavorites //收藏文件夾 
case SmartAlbumTimelapses //延時視頻文件夾,同時也會出現(xiàn)在視頻文件夾中 
case SmartAlbumAllHidden //包含隱藏照片或視頻的文件夾 
case SmartAlbumRecentlyAdded //相機近期拍攝的照片或視頻 
case SmartAlbumBursts //連拍模式拍攝的照片,在 iPad mini 上按住快門不放就可以了,但是照片依然沒有存放在這個文件夾下,而是在相機相冊里。 
case SmartAlbumSlomoVideos //Slomo 是 slow motion 的縮寫,高速攝影慢動作解析,在該模式下,iOS 設(shè)備以120幀拍攝。不過我的 iPad mini 不支持,沒法驗證。 case SmartAlbumUserLibrary //這個命名最神奇了,就是相機相冊,所有相機拍攝的照片或視頻都會出現(xiàn)在該相冊中,而且使用其他應(yīng)用保存的照片也會出現(xiàn)在這里。 
case Any //包含所有類型}

注意,獲取指定類型的相冊時,主類型和子類型要匹配,如果不匹配,系統(tǒng)會按照Any子類型來處理,對于Moment類型,子類型使用Any.
1.獲取用戶自己建立的相冊和文件夾我稱之為邏輯相冊,非系統(tǒng)相冊和從 iTunes 同步來的相冊)有兩種方法:

PHCollection.fetchTopLevelUserCollectionsWithOptions(nil)
PHAssetCollection.fetchAssetCollectionsWithType(.Album,subtype:.AlbumRegular,option:nil)

在沒有提供PHOptions的情況下,返回的PHFetchResult結(jié)果是按相冊的建立時間排序的,最新的在前面
2.獲取相機相冊:

PHAssetCollection.fetchAssetCollectionWithType(.SmartAlbum,subtype:.SmartAlbumUserLibrary,options:nil)

另外:PHAsset
的獲取方式在 iOS 8.1 后發(fā)生了一些變化。以下的兩個方法在 iOS 8.1后不再包含從 iTunes 同步以及在 iCloud 中的照片和視頻。要獲取 iOS 設(shè)備上本地的所有照片和資源只能從 PHAssetCollection 入手了。

+ fetchAssetsWithMediaType:options:
+ fetchAssetsWithOptions:

添加、刪除、編輯

對照片庫進行操作,可參見官方文檔Requesting Changes to the Photo Library,照片庫中的資源都有對應(yīng)的變更請求類:PHAssetChangeRequest,PHAssetCollectionChangeRequestPHCollectionListChangeRequest,而這些操作的請求都要求在PHPhotoLibrary的performChanges(_ changeBlock: dispatch_block_t!, completionHandler completionHandler: ((Bool, NSError!) -> Void)!)中的changeBlock中執(zhí)行。注意這里只是發(fā)出請求并沒有做出實質(zhì)的更改,因此想要根據(jù)更改結(jié)果更新UI的話不要在completionHandler中進行,而應(yīng)該在photoLibraryDidChange(changeInfo:PHChange!)中進行。三種變更請求中,刪除和編輯操作都比較簡單,而添加操作有需要注意的地方。

添加操作:placeholder的用處

在相冊中添加照片:

let createAssetRequest = PHAssetChangeRequest.creationRequestForAssetFromImage(image)
let assetPlaceholder = createAssetRequest.placeholderForCreatedAsset
let albumChangeRequest = PHAssetCollectionChangeRequest(for AssetCollection:album)
albumChangeRequest.addAssets([assetPlaceholder])

在文件夾中添加相冊:

let fetchResult = PHCollection.fetchCollectionsInCollectionList(collectionList,options:nil)
let createSubAlbumRequest = PHAssetCollectionChangeRequest.creationRequestForAssetCollectionWithTitle(title!)
let albumPlaceholder = createSubAlbumRequest.placeholderForCreatedAssetCollection
let folderChangeRequest = PHCollectionListChangeRequest.init(forCollectionList:collectionList,childCollections:fetchResult)
folderChangeRequest?.addChildCollections([albumPlaceholder])

在文件夾中添加子文件夾:

let fetchResult = PHCollection.fetchCollectionsInCollectionList(collectionList,options:nil)
let createSubAlbumRequest = PHAssetCollectionChangeRequest.creationRequestForCollectionListWithTitle(title!)
let subfolderPlaceholder = createSubFolderRequest.placeholderForCreatedCollectionList
let folderChangeRequest = PHCollectionListChangeRequest.init(forCollectionList:collectionList,childCollections:fetchResult)
folderChangeRequest?.addChildCollections([subfolderPlaceholder])

處理變更

對相冊發(fā)出變更請求后,系統(tǒng)會通知用戶是否允許,用戶允許后才會發(fā)生實質(zhì)上的變化,系統(tǒng)會發(fā)布通知。
首先,注冊成為PHPhotoLibrary的觀察者來接收變化通知:

PHPhotoLibrary.shareLibrary().registerChangeObserver(self)

然后,實現(xiàn)PHPhotoLibraryChangeObserver協(xié)議的photoLibraryDidChange(changeInfo:PHChange!)。官方一個很好的例子:Handing Changes:An Example,有以下幾點需要注意:

  • 在photoLibraryDidChange(changeInfo:PHChange!)的實現(xiàn)里將所有處理放在主線程里處理;
  • 所有PHPhotoLibrary的觀察者都會收到通知,不管觀察者本身引用的內(nèi)容是否發(fā)生變化,因此要根據(jù)觀察者的情況來對通知進行過濾。從參數(shù)PhChange對象里能獲得所有的變化,通過changeDetailsForObject:和changeDetailsForFetchResult:來獲取細節(jié)。changeDetailsForObject:獲取的細節(jié)只是PHobject子類對象本身的信息變化。對一個PHFetchResult對象使用changeDetailsForFetchResult:獲取的細節(jié)中只包含該PHFetchResult對象變化的信息,可以利用這點來對通知進行過濾處理。
  • 通過 changeDetailsForFetchResult:獲取的PHFetchResultChangeDetails對象,包含了FetchResult的結(jié)果的所有變化情況以及FetchResult的成員變化前后的數(shù)據(jù),需要注意的是成員變化的通知。
    例如,通過
var rootCollectionsFetchResult = PHCollection.fetchTopLevelUserCollectionsWithOptions(nil)

獲取所有用戶建立的相冊和文件夾,在photoLibraryDidChange(changeInfo:PHChange!)中通過以下方法獲得PHFetchResultChangeDetails對象。

let fetchChangeDetails = changeInstance.changeDetailsForFetchResult(rootCollectionsFetchResult)

fetchChangeDetails.changeObject返回一組其內(nèi)容或元數(shù)據(jù)發(fā)生變化的成員,返回的成員是跟新后的成員對象。當(dāng)用戶對某個文件夾內(nèi)的相冊或子文件夾進行添加、刪除和編輯操作即文件夾的內(nèi)容而不是文件夾本身的屬性發(fā)生變化時,通知中會該變化的信息,實際上只有在文件夾中添加相冊或子文件夾時才會在fetchChangeDetails.changedObject中有所反應(yīng),而刪除成員或是修改元數(shù)據(jù)等操作都不會再通知中有所反應(yīng),你需要使用其他手段來跟蹤變化

獲得所有相簿的原圖

(void)getOriginalImage
{
  //獲得所有的自定義相簿
PHFetchResult<PHAssetCollection *> * assetCollections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionAlbumRegular options:nil];
//遍歷所有的自定義的相簿
for(PHAssetCollection * assetCollection in assetCollections){
  [self enumerateAssetsInAssetCollection:assetCollection original:yes];
}
  //獲得相機膠卷
  PHAssetCollection * cameraRoll = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary options:nil].lastObject;
//遍歷相機膠卷,獲取大圖
[self enumerateAssetsInAssetCollection:cameraRoll original:YES];
}

獲得所有相簿中的縮略圖

- (void)getThumbnailImages
{
  //獲得所有的自定義相簿
PHFetchResult <PHAssetCollection *> assetCollections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
//遍歷所有的自定義相簿
for(PHAssetCollection * assetCollection in assetCollections){
  [self enumerateAssetsInAssetCollection:assetCollection original:NO];
}
// 獲得相機膠卷 
PHAssetCollection *cameraRoll = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary options:nil].lastObject; 
[self enumerateAssetsInAssetCollection:cameraRoll original:NO];
}

遍歷相冊

/** 
* 遍歷相簿中的所有圖片 
* @param assetCollection 相簿 
* @param original 是否要原圖 
*/
- (void)enumerateAssetsInAssetCollection:(PHAssetCollection *)assetCollection original:(BOOL)original{
 NSLog(@"相簿名:%@", assetCollection.localizedTitle);
 PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init]; 
// 同步獲得圖片, 只會返回1張圖片 
options.synchronous = YES;
 // 獲得某個相簿中的所有PHAsset對象 
PHFetchResult<PHAsset *> *assets = [PHAsset fetchAssetsInAssetCollection:assetCollection options:nil];
 for (PHAsset *asset in assets) { 
// 是否要原圖 
CGSize size = original ? CGSizeMake(asset.pixelWidth, asset.pixelHeight) : CGSizeZero; 
// 從asset中獲得圖片
 [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:size contentMode:PHImageContentModeDefault options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
 NSLog(@"%@", result); 
}];
 }}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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