關(guān)于Photos庫的簡(jiǎn)單應(yīng)用,篩選、獲取、GIF、livePhoto、video、GIF存儲(chǔ)到相冊(cè)等


寫在最開始

由于本項(xiàng)目中對(duì)UI還原度要求較高,交互要求完全還原,用別人封裝的改起來總歸是有些別扭;為了后續(xù)方便自己實(shí)現(xiàn)定制UI交互等,決定自己從系統(tǒng)API開始封裝一套相冊(cè)資源選擇器。

而AL用起來則到處報(bào)被棄用的??,想逼死我這個(gè)強(qiáng)迫癥啊~
然后就選擇了PH,總的來說和AL比較類似,但是很多東西實(shí)現(xiàn)起來卻是缺這少那的。雖說磨了有段時(shí)間,但目前用起來效果&性能尚可;下邊分享點(diǎn)小坑和核心代碼。

  • 一、簡(jiǎn)單實(shí)現(xiàn)拉取所有相冊(cè)資源(照片視頻等),并保持創(chuàng)建時(shí)間排序
  • 二、篩選大小,剔除不符合規(guī)則視頻
  • 三、gif區(qū)分、處理以及一些猜測(cè)(希望有朋友能幫忙驗(yàn)證最后的猜測(cè))
  • 四、網(wǎng)絡(luò)GIF圖存儲(chǔ)到相冊(cè)

一、 其實(shí)一開始就遇到了個(gè)問題,不能同時(shí)獲取照片和視頻。。。

最后為分別拉出加到同一個(gè)數(shù)組之后,進(jìn)行按時(shí)間排序??。

NSMutableArray<PHAsset *> *assets = [NSMutableArray array];
    PHFetchOptions *option = [[PHFetchOptions alloc] init];
    //ascending 為YES時(shí),按照照片的創(chuàng)建時(shí)間升序排列;為NO時(shí),則降序排列
    option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
    PHFetchResult *result = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:option];
    WS(ws);
    [result enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        
        PHAsset *asset = (PHAsset *)obj;
        [ws.allAssetItemModelArr addObject:[YTMediaSelectorItemModel initWithAssetType:1 itemAsset:asset withAssetLocalIdentifier:asset.localIdentifier]];
        [assets addObject:asset];
    }];
    PHFetchOptions *option = [[PHFetchOptions alloc] init];
    option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
    PHFetchResult *result = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeVideo options:option];
    NSLog(@"拉取到 %ld 個(gè)視頻資源\n", result.count);
    WS(ws);
    [result enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        PHAsset *asset = (PHAsset *)obj;
        [ws.allAssetItemModelArr addObject:[YTMediaSelectorItemModel initWithAssetType:2 itemAsset:asset withAssetLocalIdentifier:asset.localIdentifier]];

    }];
- (NSArray *)comparaAssetsModelArr:(NSMutableArray <YTMediaSelectorItemModel *>*)assetModels{
    
    NSComparator cmptr = ^(YTMediaSelectorItemModel *obj1, YTMediaSelectorItemModel *obj2){
        if ([obj1.itemAsset.creationDate timeIntervalSince1970] < [obj2.itemAsset.creationDate timeIntervalSince1970]) {
            return (NSComparisonResult)NSOrderedDescending;
        }
        
        if ([obj1.itemAsset.creationDate timeIntervalSince1970] > [obj2.itemAsset.creationDate timeIntervalSince1970]) {
            return (NSComparisonResult)NSOrderedAscending;
        }
        return (NSComparisonResult)NSOrderedSame;
    };
    self.allAssetItemModelArr = [[assetModels sortedArrayUsingComparator:cmptr] mutableCopy];
    return self.allAssetItemModelArr;
}

YTMediaSelectorItemModel為自己包裝的對(duì)象,方便本地做標(biāo)記和取用。

@interface YTMediaSelectorItemModel : NSObject

/// 1 image   2 video   3 gif
@property (nonatomic, assign) NSInteger assetType;
@property (nonatomic, strong) PHAsset *itemAsset;
@property (nonatomic, copy) NSString *astLocalIdentifier;
@property (nonatomic, strong) UIImage *thumbnailImg;
/** 是否被用戶勾選 */
@property (nonatomic, assign) BOOL isSelected;
/** 當(dāng)前元素 被選中之后的標(biāo)號(hào) */
@property (nonatomic, copy) NSString *currentItemSelectedFlage;
@property (nonatomic, assign) long long videoTimeLength;

+ (YTMediaSelectorItemModel *)initWithAssetType:(NSInteger)assetType itemAsset:(PHAsset *)asset withAssetLocalIdentifier:(NSString *)localIdentifier;
@end

二、 這么搞完一套,就取出了所有的資源,但是若想在構(gòu)造YTMediaSelectorItemModel時(shí)再做些篩選的話,就會(huì)碰到各種問題了??。


  • 比如想篩選視頻大小,限制一個(gè)范圍的時(shí)候
    此時(shí)我們肯定會(huì)去看下Asset里有無fileSize之類的屬性,我看完傷心了,沒有。。。
    PHAsset頭文件

    也就是說無法直接取其屬性進(jìn)行計(jì)算比對(duì)了,多番查找發(fā)現(xiàn)有個(gè)PHAssetResource類,ta有fileSize的私有屬性:
/*
         PHAssetResource:
                         type:
                         uti: 
                         filename: 
                         asset: 
                         locallyAvailable: 
                         fileURL: 
                         width: 
                         height: 
                         fileSize: 1924730
                         analysisType: never-download
                         cplResourceType: Original
                         isCurrent: YES
                         isInCloud: NO
         */

繼續(xù)查找怎么獲取這個(gè)asset的Resource有如此一行代碼:

[[PHAssetResource assetResourcesForAsset:asset] firstObject]

此時(shí)我們 valueForKey一下就拿到了想要的數(shù)據(jù),暫時(shí)實(shí)現(xiàn)了既定需求。
如此就結(jié)束了嗎???沒這么簡(jiǎn)單。。。
打印log發(fā)現(xiàn),每執(zhí)行100次左右的assetResourcesForAsset:就會(huì)花大概一秒的時(shí)間?。。??嘗試有無別的方式獲取fileSize無果,我妥協(xié)了、向現(xiàn)實(shí)低了頭,我做了當(dāng)需要篩選fileSize的時(shí)候進(jìn)行分批回調(diào)。。。

__block int count = 0;
[result enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        count ++;
        if (count > 0 && count % 50 == 0) {
            NSLog(@"在篩選fileSize時(shí),分批回調(diào),先回調(diào)第%d批\n",count / 50);
            if (ws.ytSelectorTakedAllMediaBlock) {
                ws.ytSelectorTakedAllMediaBlock(ws.allAssetItemModelArr);
            }
        }
}];

如此,篩選fileSize時(shí)也能有類似秒開秒顯的感覺了,勉強(qiáng)算是實(shí)現(xiàn)需求吧~~~


以上是19年寫的代碼,感覺沒啥東西好說;而今(2021.03.17)需要對(duì)GIF做些處理,查找GIF相關(guān)知識(shí)點(diǎn)時(shí)發(fā)現(xiàn)都是東拉西扯,基本沒有關(guān)于PHAsset拿到之后構(gòu)造顯示數(shù)據(jù)源時(shí)就做好標(biāo)記進(jìn)行區(qū)分的文章,嘗試自己搞搞~~~并分享下吧,希望能幫助到一些后來者.

三、 下面開始正經(jīng)的說下怎么通過PHAsset區(qū)分GIF、livePhoto等

先說livePhoto、HDR、截圖之類,有對(duì)應(yīng)的mediaSubtypes字段返回一個(gè)枚舉值:

typedef NS_OPTIONS(NSUInteger, PHAssetMediaSubtype) {
    PHAssetMediaSubtypeNone               = 0,
    
    // Photo subtypes
    PHAssetMediaSubtypePhotoPanorama      = (1UL << 0),
    PHAssetMediaSubtypePhotoHDR           = (1UL << 1),
    PHAssetMediaSubtypePhotoScreenshot API_AVAILABLE(ios(9)) = (1UL << 2),
    PHAssetMediaSubtypePhotoLive API_AVAILABLE(ios(9.1)) = (1UL << 3),
    PHAssetMediaSubtypePhotoDepthEffect API_AVAILABLE(macos(10.12.2), ios(10.2), tvos(10.1)) = (1UL << 4),

    
    // Video subtypes
    PHAssetMediaSubtypeVideoStreamed      = (1UL << 16),
    PHAssetMediaSubtypeVideoHighFrameRate = (1UL << 17),
    PHAssetMediaSubtypeVideoTimelapse     = (1UL << 18),
};

UL ? 無符號(hào)long類型

1.下面捋捋對(duì)應(yīng)關(guān)系:

None                                =  0,
全景                                 = (1UL << 0)       1,
HDR                                 = (1UL << 1)        2,
截圖                                 = (1UL << 2)        4,
實(shí)況照片(livePhoto)                   = (1UL << 3)        8,
景深效果(DepthEffect)                 =  (1UL << 4)      16,
****                                 =                  32,
gif                                   =                 64,

視頻的 1UL << 16 開始,我不得不猜測(cè)可以用這個(gè)mediaSubtypes == 64來判斷是否為gif。當(dāng)然,這個(gè)辦法只經(jīng)過我一百來張三個(gè)渠道的GIF測(cè)試,可能會(huì)有遺漏之類(期待大家的驗(yàn)證,歡迎驗(yàn)證通過的朋友在評(píng)論區(qū)留下一筆)。

  1. 判斷PHAsset是否GIF第二種:
[[asset valueForKey:@"filename"] hasSuffix:@"GIF"]

判斷后綴是否為GIF / gif。也通過了我那一百多張GIF的測(cè)試。

  1. 取PHAsset私有屬性u(píng)niformTypeIdentifier,和第二種類似,但似乎更靠譜一點(diǎn)
[[asset valueForKey:@"uniformTypeIdentifier"] isEqual:@"com.compuserve.gif"]
  1. 判斷PHAsset是否GIF第四種:
[[PHAssetResource assetResourcesForAsset:asset].firstObject.uniformTypeIdentifier isEqualToString:@"com.compuserve.gif"]

也是我認(rèn)為最正經(jīng)的一種辦法,但是又面臨一個(gè)執(zhí)行耗時(shí)的問題。也是100次耗時(shí)1秒左右!??!

四種方式:1 2 3都不影響速度,4看著最正經(jīng)但是費(fèi)時(shí)。。。最后我選擇了第三種方式??


期待用了第一種方式的朋友和我互通有無問題

四、 關(guān)于存儲(chǔ)GIF動(dòng)圖到相冊(cè)

最常見的把image存入相冊(cè)的操作莫過于 UIImageWriteToSavedPhotosAlbum
這個(gè)API了吧?然鵝、GIF圖通過SDWebImage的loadImageWithURL獲取到的為image對(duì)象,直接writeToSavedPhotosAlbum將保存一張靜態(tài)圖。怎么辦呢?去找了下PHotos里的API,找到了如下代碼:

NSError *err = nil;
[[PHPhotoLibrary sharedPhotoLibrary] performChangesAndWait:^{
    PHAssetResourceCreationOptions *options = [[PHAssetResourceCreationOptions alloc] init];
    [[PHAssetCreationRequest creationRequestForAsset] addResourceWithType:PHAssetResourceTypePhoto data:imgData options:options];
} error:&error];

以為這個(gè)問題到這就解決了,實(shí)際還有個(gè)小問題:SDWebImage的loadImageWithURL加載圖片結(jié)束的回調(diào)里并不會(huì)返回data,其值為nil;而PHPhotoLibrary里的API需要data,嘗試調(diào)整SDWebImageOptions字段,發(fā)現(xiàn)并不能實(shí)現(xiàn)直接回調(diào)image對(duì)應(yīng)的data。
*一個(gè)好的程序員當(dāng)以解決問題為第一要?jiǎng)?wù),想辦法取SD緩存到本地的data!有了如下一行:

  NSData *imgData = [[SDImageCache sharedImageCache] diskImageDataForKey:imageUrl];

此處我的SDWebImage版本為5.0以后版本,之前版本取本地data略有不同,自行解決。

最后編輯于
?著作權(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)容