保存圖片到自定義相冊(cè)
<b style="color:red">
實(shí)際上,自定義相冊(cè)中的圖片并不是實(shí)際的圖片,而是對(duì)系統(tǒng)【相機(jī)膠卷】這個(gè)相冊(cè)中的圖片進(jìn)行了一個(gè)引用。所以將圖片保存到自定義相冊(cè)的第一步就是先保存到系統(tǒng)的【相機(jī)膠卷】中。
</b>
1 步驟
-
保存到系統(tǒng)的相冊(cè)【相機(jī)膠卷】中
(1)C語言函數(shù)來保存 (2)AssetsLibrary框架--系統(tǒng)自帶,iOS9廢棄 (3)Photos框架--系統(tǒng)自帶,iOS8即可使用,取代AssetsLibrary -
擁有自定義相冊(cè)(如果沒有,則創(chuàng)建)
AssetsLibrary Photos -
將圖片添加到自定義相冊(cè)中
AssetsLibrary Photos
2 Photos 框架簡(jiǎn)單介紹
2.1 重要的類
該框架有幾個(gè)非常重要的類:PHAsset、PHAssetCollection 和 PHLibrary。
- PHAsset 表示一個(gè)圖片或者視頻文件(存儲(chǔ)在手機(jī)的照片 APP 中的)。與具體圖片有關(guān)的使用這個(gè)類
- PHAssetCollection 表示圖片集合或者視頻集合,其實(shí)就是指相冊(cè)(包括系統(tǒng)相冊(cè)和自定義相冊(cè))
- PHLibrary 表示整個(gè)相冊(cè)庫(kù),包括整個(gè)相冊(cè)和圖片等
<b style="color:red">
只要與單個(gè)圖片相關(guān),使用 PHAsset。只要與相冊(cè)相關(guān),使用 PHAssetCollection
</b>
2.2 查詢操作
查詢操作,直接使用 PHAsset 和 PHAssetCollection 類本身的方法
//1 獲取相冊(cè)中的圖片--傳入相冊(cè)圖片的 ID---返回一組圖片
[PHAsset fetchAssetsWithLocalIdentifiers:@[ID] options:nil];
//2 查詢手機(jī)中所有的相冊(cè)列表(分為系統(tǒng)相冊(cè)和自定義相冊(cè),通過控制傳入的參數(shù)來確定)---返回相冊(cè)組--類似數(shù)組-forin遍歷即可
[PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
2.3 增刪改操作(除了獲取之外所有的操作)
<b style="color:red">
1 如果做查詢之外的操作,比如說保存圖片、創(chuàng)建自定義相冊(cè)、向自定義相冊(cè)中添加圖片等,都需要使用另外兩個(gè)類:PHAssetChangeRequest 和 PHAssetCollectionChangeRequest
2 這些操作必須在 [[PHPhotoLibrary sharedPhotoLibrary]performChange...]的 block 中間調(diào)用
</b>
//1 保存圖片
[PHAssetChangeRequest creationRequestForAssetFromImage:self.imageView.image];
//2 創(chuàng)建相冊(cè)
[PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:@"自定義相冊(cè)"];
3 將圖片保存到系統(tǒng)相冊(cè)【相機(jī)膠卷】中
3.1 C語言函數(shù)保存
點(diǎn)擊保存按鈕后的代碼:
//1 把圖片保存到系統(tǒng)相冊(cè)中,結(jié)束后調(diào)用 image:didFinishSavingWithError:contextInfo:方法(回調(diào)方法)
//2 回調(diào)方法的格式有要求,可以進(jìn)入頭文件查看
UIImageWriteToSavedPhotosAlbum(self.imageView.image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
實(shí)現(xiàn)回調(diào)方法
-(void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
if(error)
{
NSLog(@"保存圖片失敗");
return;
}
NSLog(@"保存圖片成功");
}
3.2 Photos 框架保存圖片到系統(tǒng)相冊(cè)
Photos 框架保存圖片 --- 使用 PHAssetChangeRequest 類 方法
有兩種方式;異步方式和同步方式,但是保存圖片的操作性能消耗不大,所以可以直接使用同步方式
3.2.1 異步方式保存圖片
//異步保存圖片
-(void)asyncSaveImageWithPhotos
{
//1 必須在 block 中調(diào)用
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
//2 異步執(zhí)行保存圖片操作
[PHAssetChangeRequest creationRequestForAssetFromImage:self.imageView.image];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
//3 保存結(jié)束后,回調(diào)
if (error) {
[SVProgressHUD showErrorWithStatus:@"保存失敗"];
}else
[SVProgressHUD showSuccessWithStatus:@"保存成功"];
}];
}
#######3.2.2 同步方式保存圖片
下面的例子是通過保存時(shí)刻的占位 id 來獲取圖像,其實(shí)也可以直接返回占位圖片。后面的操作可以直接進(jìn)使用占位圖片代替圖片
/**同步方式保存圖片到系統(tǒng)的相機(jī)膠卷中---返回的是當(dāng)前保存成功后相冊(cè)圖片對(duì)象集合*/
-(PHFetchResult<PHAsset *> *)syncSaveImageWithPhotos
{
//--1 創(chuàng)建 ID 這個(gè)參數(shù)可以獲取到圖片保存后的 asset對(duì)象
__block NSString *createdAssetID = nil;
//--2 保存圖片
NSError *error = nil;
[[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
//----block 執(zhí)行的時(shí)候還沒有保存成功--獲取占位圖片的 id,通過 id 獲取圖片---同步
createdAssetID = [PHAssetChangeRequest creationRequestForAssetFromImage:self.imageView.image].placeholderForCreatedAsset.localIdentifier;
} error:&error];
//--3 如果失敗,則返回空
if (error) {
return nil;
}
//--4 成功后,返回對(duì)象
//獲取保存到系統(tǒng)相冊(cè)成功后的 asset 對(duì)象集合,并返回
PHFetchResult<PHAsset *> *assets = [PHAsset fetchAssetsWithLocalIdentifiers:@[createdAssetID] options:nil];
return assets;
}
4 擁有自定義相冊(cè)(如果沒有,則創(chuàng)建)
下面的例子是:如果沒有,則常見跟當(dāng)前 APP 同名的自定義相冊(cè)
實(shí)現(xiàn)思路:
① 獲取當(dāng)前的 APP 的 BundleName
② 使用PHAssetCollection的fetchAssetCollectionsWithType:subType:options方法,通過傳入類型,獲取所有的自定義相冊(cè)列表
③ 遍歷獲取的自定義相冊(cè)列表,與APP的名稱進(jìn)行比對(duì),匹配后返回當(dāng)前同名的自定義相冊(cè) PHAssetCollection對(duì)象
④ 如果沒有找到,則開始創(chuàng)建,在PHPhotoLibrary單例對(duì)象的perfromChange方法中執(zhí)行創(chuàng)建自定義相冊(cè)操作
⑤ block中,保存待創(chuàng)建相冊(cè)的占位標(biāo)識(shí)符---其實(shí)這個(gè)時(shí)刻,相冊(cè)根本沒創(chuàng)建完成
⑥ 通過error判斷是否創(chuàng)建成功,
⑦ 如果創(chuàng)建成功,通過PHAssetCollection的fetchAssetCollectionsWithLocalIdentifiers:options來獲取當(dāng)前創(chuàng)建相冊(cè)對(duì)象PHAssetCollection
/**擁有與 APP 同名的自定義相冊(cè)--如果沒有則創(chuàng)建*/
-(PHAssetCollection *)getAssetCollectionWithAppNameAndCreateIfNo
{
//1 獲取以 APP 的名稱
NSString *title = [NSBundle mainBundle].infoDictionary[(__bridge NSString *)kCFBundleNameKey];
//2 獲取與 APP 同名的自定義相冊(cè)
PHFetchResult<PHAssetCollection *> *collections = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
for (PHAssetCollection *collection in collections) {
//遍歷
if ([collection.localizedTitle isEqualToString:title]) {
//找到了同名的自定義相冊(cè)--返回
return collection;
}
}
//說明沒有找到,需要?jiǎng)?chuàng)建
NSError *error = nil;
__block NSString *createID = nil; //用來獲取創(chuàng)建好的相冊(cè)
[[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
//發(fā)起了創(chuàng)建新相冊(cè)的請(qǐng)求,并拿到ID,當(dāng)前并沒有創(chuàng)建成功,待創(chuàng)建成功后,通過 ID 來獲取創(chuàng)建好的自定義相冊(cè)
PHAssetCollectionChangeRequest *request = [PHAssetCollectionChangeRequest creationRequestForAssetCollectionWithTitle:title];
createID = request.placeholderForCreatedAssetCollection.localIdentifier;
} error:&error];
if (error) {
[SVProgressHUD showErrorWithStatus:@"創(chuàng)建失敗"];
return nil;
}else{
[SVProgressHUD showSuccessWithStatus:@"創(chuàng)建成功"];
//通過 ID 獲取創(chuàng)建完成的相冊(cè) -- 是一個(gè)數(shù)組
return [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[createID] options:nil].firstObject;
}
}
5 將圖片對(duì)象添加到自定義相冊(cè)中
實(shí)現(xiàn)思路
① 使用獲取的自定義相冊(cè)來創(chuàng)建PHAssetCollection對(duì)象
② 將剛才保存到系統(tǒng)相冊(cè)的PHAsset保存到相冊(cè)中
/**將圖片保存到自定義相冊(cè)中*/
-(void)saveImageToCustomAblum
{
//1 將圖片保存到系統(tǒng)的【相機(jī)膠卷】中---調(diào)用剛才的方法
PHFetchResult<PHAsset *> *assets = [self syncSaveImageWithPhotos];
if (assets == nil)
{
[SVProgressHUD showErrorWithStatus:@"保存失敗"];
return;
}
//2 擁有自定義相冊(cè)(與 APP 同名,如果沒有則創(chuàng)建)--調(diào)用剛才的方法
PHAssetCollection *assetCollection = [self getAssetCollectionWithAppNameAndCreateIfNo];
if (assetCollection == nil) {
[SVProgressHUD showErrorWithStatus:@"相冊(cè)創(chuàng)建失敗"];
return;
}
//3 將剛才保存到相機(jī)膠卷的圖片添加到自定義相冊(cè)中 --- 保存帶自定義相冊(cè)--屬于增的操作,需要在PHPhotoLibrary的block中進(jìn)行
NSError *error = nil;
[[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
//--告訴系統(tǒng),要操作哪個(gè)相冊(cè)
PHAssetCollectionChangeRequest *collectionChangeRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:assetCollection];
//--添加圖片到自定義相冊(cè)--追加--就不能成為封面了
//--[collectionChangeRequest addAssets:assets];
//--插入圖片到自定義相冊(cè)--插入--可以成為封面
[collectionChangeRequest insertAssets:assets atIndexes:[NSIndexSet indexSetWithIndex:0]];
} error:&error];
if (error) {
[SVProgressHUD showErrorWithStatus:@"保存失敗"];
return;
}
[SVProgressHUD showSuccessWithStatus:@"保存成功"];
}
6 相冊(cè)的授權(quán)訪問
當(dāng)?shù)谝淮问褂肁PP的時(shí)候,或者第一次訪問相冊(cè)的時(shí)候,系統(tǒng)會(huì)彈出授權(quán)選擇對(duì)話框詢問用戶。所以我們?cè)诒4鎴D片到相冊(cè)的時(shí)候,需要判斷當(dāng)前是否授權(quán)。
6.1 權(quán)限分類
PHAuthorizationStatusNotDetermined ,---用戶之前還未決定
PHAuthorizationStatusRestricted, ---系統(tǒng)問題,用戶沒有權(quán)限決定--比如家長(zhǎng)控制器模式
PHAuthorizationStatusDenied,---用戶之前拒絕過
PHAuthorizationStatusAuthorized --用戶允許
6.2 請(qǐng)求權(quán)限的方式
可以使用NSPhotoLibrary的類方法requestAuthorization來查看權(quán)限,或者請(qǐng)求權(quán)限。如果用戶之前沒做決定,則彈出系統(tǒng)對(duì)話框請(qǐng)求權(quán)限;如果用戶做過決定,則調(diào)用該類方法的block。
/*
1 block 調(diào)用時(shí)刻---這個(gè)實(shí)在子線程中調(diào)用的
--1.1 如果用戶第一次打開 APP,之前決定過權(quán)限,則彈出系統(tǒng)框,讓用戶選擇權(quán)限。---選擇之后才會(huì)調(diào)用 block,并把剛才選擇的結(jié)果一并傳入
--1.2 如果用戶之前已經(jīng)決定過權(quán)限,則直接調(diào)用 block,并把之前選擇的結(jié)果傳入
2 state 類型
PHAuthorizationStatusNotDetermined ,---用戶之前還未決定,直接彈出系統(tǒng)對(duì)話框,這個(gè) state 不會(huì)傳給 block,會(huì)傳入用戶選擇的結(jié)果
PHAuthorizationStatusRestricted, ---系統(tǒng)問題,用戶沒有權(quán)限決定--比如家長(zhǎng)控制器模式
PHAuthorizationStatusDenied,---用戶之前拒絕過
PHAuthorizationStatusAuthorized --用戶允許,直接調(diào)用 block,傳入該狀態(tài)
**/
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
//當(dāng)前的block的調(diào)用是在子線程,需要回到主線程來操作
}
6.3 例子
點(diǎn)擊保存圖片按鈕后的操作---一個(gè)完整的將圖片保存到自定義相冊(cè)的操作
-(void)save
{
//(1) 獲取當(dāng)前的授權(quán)狀態(tài)
PHAuthorizationStatus lastStatus = [PHPhotoLibrary authorizationStatus];
//(2) 請(qǐng)求授權(quán)
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
//回到主線程
dispatch_async(dispatch_get_main_queue(), ^{
if(status == PHAuthorizationStatusDenied) //用戶拒絕(可能是之前拒絕的,有可能是剛才在系統(tǒng)彈框中選擇的拒絕)
{
if (lastStatus == PHAuthorizationStatusNotDetermined) {
//說明,用戶之前沒有做決定,在彈出授權(quán)框中,選擇了拒絕
[SVProgressHUD showErrorWithStatus:@"保存失敗"];
return;
}
// 說明,之前用戶選擇拒絕過,現(xiàn)在又點(diǎn)擊保存按鈕,說明想要使用該功能,需要提示用戶打開授權(quán)
[SVProgressHUD showInfoWithStatus:@"失?。≌?qǐng)?jiān)谙到y(tǒng)設(shè)置中開啟訪問相冊(cè)權(quán)限"];
}
else if(status == PHAuthorizationStatusAuthorized) //用戶允許
{
//保存圖片---調(diào)用上面封裝的方法
[self saveImageToCustomAblum];
}
else if (status == PHAuthorizationStatusRestricted)
{
[SVProgressHUD showErrorWithStatus:@"系統(tǒng)原因,無法訪問相冊(cè)"];
}
});
}];
}