保存圖片到自定義相冊(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語(yǔ)言函數(shù)來(lái)保存 (2)AssetsLibrary框架--系統(tǒng)自帶舟扎,iOS9廢棄 (3)Photos框架--系統(tǒng)自帶遥巴,iOS8即可使用千康,取代AssetsLibrary
-
擁有自定義相冊(cè)(如果沒(méi)有,則創(chuàng)建)
AssetsLibrary Photos
-
將圖片添加到自定義相冊(cè)中
AssetsLibrary Photos
2 Photos 框架簡(jiǎn)單介紹
2.1 重要的類(lèi)
該框架有幾個(gè)非常重要的類(lèi):PHAsset铲掐、PHAssetCollection 和 PHLibrary拾弃。
- PHAsset 表示一個(gè)圖片或者視頻文件(存儲(chǔ)在手機(jī)的照片 APP 中的)。與具體圖片有關(guān)的使用這個(gè)類(lèi)
- 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 查詢(xún)操作
查詢(xún)操作,直接使用 PHAsset 和 PHAssetCollection 類(lèi)本身的方法
//1 獲取相冊(cè)中的圖片--傳入相冊(cè)圖片的 ID---返回一組圖片
[PHAsset fetchAssetsWithLocalIdentifiers:@[ID] options:nil];
//2 查詢(xún)手機(jī)中所有的相冊(cè)列表(分為系統(tǒng)相冊(cè)和自定義相冊(cè)婉支,通過(guò)控制傳入的參數(shù)來(lái)確定)---返回相冊(cè)組--類(lèi)似數(shù)組-forin遍歷即可
[PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
2.3 增刪改操作(除了獲取之外所有的操作)
<b style="color:red">
1 如果做查詢(xún)之外的操作鸯隅,比如說(shuō)保存圖片、創(chuàng)建自定義相冊(cè)向挖、向自定義相冊(cè)中添加圖片等蝌以,都需要使用另外兩個(gè)類(lèi):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語(yǔ)言函數(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 類(lèi) 方法
有兩種方式;異步方式和同步方式跟畅,但是保存圖片的操作性能消耗不大,所以可以直接使用同步方式
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 同步方式保存圖片
下面的例子是通過(guò)保存時(shí)刻的占位 id 來(lái)獲取圖像碍彭,其實(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í)候還沒(méi)有保存成功--獲取占位圖片的 id,通過(guò) 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è)(如果沒(méi)有皆疹,則創(chuàng)建)
下面的例子是:如果沒(méi)有疏橄,則常見(jiàn)跟當(dāng)前 APP 同名的自定義相冊(cè)
實(shí)現(xiàn)思路:
① 獲取當(dāng)前的 APP 的 BundleName
② 使用PHAssetCollection的fetchAssetCollectionsWithType:subType:options方法,通過(guò)傳入類(lèi)型略就,獲取所有的自定義相冊(cè)列表
③ 遍歷獲取的自定義相冊(cè)列表捎迫,與APP的名稱(chēng)進(jìn)行比對(duì),匹配后返回當(dāng)前同名的自定義相冊(cè) PHAssetCollection對(duì)象
④ 如果沒(méi)有找到表牢,則開(kāi)始創(chuàng)建窄绒,在PHPhotoLibrary單例對(duì)象的perfromChange方法中執(zhí)行創(chuàng)建自定義相冊(cè)操作
⑤ block中,保存待創(chuàng)建相冊(cè)的占位標(biāo)識(shí)符---其實(shí)這個(gè)時(shí)刻崔兴,相冊(cè)根本沒(méi)創(chuàng)建完成
⑥ 通過(guò)error判斷是否創(chuàng)建成功彰导,
⑦ 如果創(chuàng)建成功,通過(guò)PHAssetCollection的fetchAssetCollectionsWithLocalIdentifiers:options來(lái)獲取當(dāng)前創(chuàng)建相冊(cè)對(duì)象PHAssetCollection
/**擁有與 APP 同名的自定義相冊(cè)--如果沒(méi)有則創(chuàng)建*/
-(PHAssetCollection *)getAssetCollectionWithAppNameAndCreateIfNo
{
//1 獲取以 APP 的名稱(chēng)
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;
}
}
//說(shuō)明沒(méi)有找到敲茄,需要?jiǎng)?chuàng)建
NSError *error = nil;
__block NSString *createID = nil; //用來(lái)獲取創(chuàng)建好的相冊(cè)
[[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
//發(fā)起了創(chuàng)建新相冊(cè)的請(qǐng)求位谋,并拿到ID,當(dāng)前并沒(méi)有創(chuàng)建成功堰燎,待創(chuàng)建成功后掏父,通過(guò) ID 來(lái)獲取創(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)建成功"];
//通過(guò) ID 獲取創(chuàng)建完成的相冊(cè) -- 是一個(gè)數(shù)組
return [PHAssetCollection fetchAssetCollectionsWithLocalIdentifiers:@[createID] options:nil].firstObject;
}
}
5 將圖片對(duì)象添加到自定義相冊(cè)中
實(shí)現(xiàn)思路
① 使用獲取的自定義相冊(cè)來(lái)創(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 同名,如果沒(méi)有則創(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)訪(fǎng)問(wèn)
當(dāng)?shù)谝淮问褂肁PP的時(shí)候,或者第一次訪(fǎng)問(wèn)相冊(cè)的時(shí)候鸟款,系統(tǒng)會(huì)彈出授權(quán)選擇對(duì)話(huà)框詢(xún)問(wèn)用戶(hù)膏燃。所以我們?cè)诒4鎴D片到相冊(cè)的時(shí)候,需要判斷當(dāng)前是否授權(quán)何什。
6.1 權(quán)限分類(lèi)
PHAuthorizationStatusNotDetermined ,---用戶(hù)之前還未決定
PHAuthorizationStatusRestricted, ---系統(tǒng)問(wèn)題组哩,用戶(hù)沒(méi)有權(quán)限決定--比如家長(zhǎng)控制器模式
PHAuthorizationStatusDenied,---用戶(hù)之前拒絕過(guò)
PHAuthorizationStatusAuthorized --用戶(hù)允許
6.2 請(qǐng)求權(quán)限的方式
可以使用NSPhotoLibrary的類(lèi)方法requestAuthorization來(lái)查看權(quán)限,或者請(qǐng)求權(quán)限处渣。如果用戶(hù)之前沒(méi)做決定伶贰,則彈出系統(tǒng)對(duì)話(huà)框請(qǐng)求權(quán)限;如果用戶(hù)做過(guò)決定罐栈,則調(diào)用該類(lèi)方法的block黍衙。
/*
1 block 調(diào)用時(shí)刻---這個(gè)實(shí)在子線(xiàn)程中調(diào)用的
--1.1 如果用戶(hù)第一次打開(kāi) APP,之前決定過(guò)權(quán)限荠诬,則彈出系統(tǒng)框琅翻,讓用戶(hù)選擇權(quán)限位仁。---選擇之后才會(huì)調(diào)用 block,并把剛才選擇的結(jié)果一并傳入
--1.2 如果用戶(hù)之前已經(jīng)決定過(guò)權(quán)限方椎,則直接調(diào)用 block聂抢,并把之前選擇的結(jié)果傳入
2 state 類(lèi)型
PHAuthorizationStatusNotDetermined ,---用戶(hù)之前還未決定,直接彈出系統(tǒng)對(duì)話(huà)框棠众,這個(gè) state 不會(huì)傳給 block琳疏,會(huì)傳入用戶(hù)選擇的結(jié)果
PHAuthorizationStatusRestricted, ---系統(tǒng)問(wèn)題,用戶(hù)沒(méi)有權(quán)限決定--比如家長(zhǎng)控制器模式
PHAuthorizationStatusDenied,---用戶(hù)之前拒絕過(guò)
PHAuthorizationStatusAuthorized --用戶(hù)允許闸拿,直接調(diào)用 block空盼,傳入該狀態(tài)
**/
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
//當(dāng)前的block的調(diào)用是在子線(xiàn)程,需要回到主線(xiàn)程來(lái)操作
}
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) {
//回到主線(xiàn)程
dispatch_async(dispatch_get_main_queue(), ^{
if(status == PHAuthorizationStatusDenied) //用戶(hù)拒絕(可能是之前拒絕的新荤,有可能是剛才在系統(tǒng)彈框中選擇的拒絕)
{
if (lastStatus == PHAuthorizationStatusNotDetermined) {
//說(shuō)明揽趾,用戶(hù)之前沒(méi)有做決定,在彈出授權(quán)框中苛骨,選擇了拒絕
[SVProgressHUD showErrorWithStatus:@"保存失敗"];
return;
}
// 說(shuō)明但骨,之前用戶(hù)選擇拒絕過(guò),現(xiàn)在又點(diǎn)擊保存按鈕智袭,說(shuō)明想要使用該功能,需要提示用戶(hù)打開(kāi)授權(quán)
[SVProgressHUD showInfoWithStatus:@"失斅犹А吼野!請(qǐng)?jiān)谙到y(tǒng)設(shè)置中開(kāi)啟訪(fǎng)問(wèn)相冊(cè)權(quán)限"];
}
else if(status == PHAuthorizationStatusAuthorized) //用戶(hù)允許
{
//保存圖片---調(diào)用上面封裝的方法
[self saveImageToCustomAblum];
}
else if (status == PHAuthorizationStatusRestricted)
{
[SVProgressHUD showErrorWithStatus:@"系統(tǒng)原因,無(wú)法訪(fǎng)問(wèn)相冊(cè)"];
}
});
}];
}