iOS-保存圖片到自定義相冊(cè)|Photos|C函數(shù)

保存圖片到自定義相冊(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è)"];
            }
        });
    }];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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