圖片選擇器(Photos / AssetsLibrary)

????????有沒(méi)有人和我一樣,在最開(kāi)始接觸iOS開(kāi)發(fā)的時(shí)候,碰到圖片選擇器用的是UIImagePickerController,后來(lái)發(fā)現(xiàn)產(chǎn)品和設(shè)計(jì)不會(huì)按照系統(tǒng)頁(yè)面來(lái)設(shè)計(jì),這個(gè)時(shí)候去github或者cocochina上找一個(gè)第三方庫(kù),修修改改,用到哪改到哪,管中窺豹,由于定制了計(jì)劃,每月寫(xiě)兩篇博客,借此機(jī)會(huì),把這個(gè)框架梳理一下。

????????首先明確一點(diǎn),這篇文章是了解一下 AssetsLibrary和Photos框架,并就實(shí)例進(jìn)行說(shuō)明解析,很簡(jiǎn)單的一個(gè)小demo,并不是現(xiàn)成拿過(guò)來(lái)直接就能用。(Photos雖然比AssetsLibrary用起來(lái)舒服太多,但是它是iOS8.0之后才出來(lái)的,注意一下適配)

????????市場(chǎng)上絕大部分圖片選擇器都大同小異,例如微信和微博:


wechat.png
微博.png

其實(shí)就是兩個(gè)列表,一個(gè)相冊(cè)列表,一個(gè)圖片列表,當(dāng)然也可以直接理解為一個(gè)tableView,一個(gè)collectionView。

Photos


1.1、UITableView

  • 授權(quán)、獲取相冊(cè)集合
    /*
     *  獲取用戶授權(quán)
     *
     *  PHAuthorizationStatus  授權(quán)狀態(tài)
     */
    [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
        
        /*
         *  PHAuthorizationStatusNotDetermined  用戶還沒(méi)有進(jìn)行授權(quán)操作
         *  PHAuthorizationStatusRestricted     用戶關(guān)閉了訪問(wèn)相冊(cè)的權(quán)限
         *  PHAuthorizationStatusDenied         拒絕用戶訪問(wèn)這個(gè)應(yīng)用程序有明確的圖片數(shù)據(jù)
         *  PHAuthorizationStatusAuthorized     用戶已授權(quán)
         */
        
        if (status == PHAuthorizationStatusNotDetermined || status == PHAuthorizationStatusRestricted) {
            /*
             *  未通過(guò)用戶授權(quán),可彈出用戶引導(dǎo),UIAlertView(請(qǐng)?jiān)谠O(shè)備的\"設(shè)置-隱私-照片\"中允許訪問(wèn)照片)
             */
        } else {
            /*
             *  用戶已授權(quán)
             *
             *  獲取相冊(cè)集合,將其加入到tableView數(shù)據(jù)源中
             */
            PHFetchResult *album = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum
                                                                            subtype:PHAssetCollectionSubtypeAlbumRegular
                                                                            options:nil];
            [album enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                PHAssetCollection *collection = (PHAssetCollection *)obj;

                // 這里需要注意下,我們數(shù)據(jù)源中存的是PHAssetCollection
                [self.groups addObject:collection]; 
                [self.tableView reloadData];
            }];
        }
    }];
  • 信息對(duì)照表
屬性 描述
PHAsset 代表照片庫(kù)中的一個(gè)資源,通過(guò) PHAsset 可以獲取和保存資源
PHFetchOptions 獲取資源時(shí)的參數(shù),可以傳 nil,即使用系統(tǒng)默認(rèn)值
PHAssetCollection PHCollection 的子類,表示一個(gè)相冊(cè)或者一個(gè)時(shí)刻(例:最近刪除,視頻列表,收藏等)
PHFetchResult 表示一系列的資源結(jié)果集合(此處是相冊(cè)集合),從PHCollection 的類方法中獲得
PHImageManager 用于處理資源的加載,加載圖片的過(guò)程帶有緩存處理,可以通過(guò)傳入一個(gè) PHImageRequestOptions 控制資源的輸出尺寸等規(guī)格(上面cell刷新時(shí)代碼)
PHImageRequestOptions 控制加載圖片時(shí)的一系列參數(shù)
  • 枚舉
    typedef NS_ENUM(NSInteger, PHAssetCollectionType) {
        PHAssetCollectionTypeAlbum      = 1,      // 從iTunes同步的相冊(cè),以及用戶在 Photos 中自己建立的相冊(cè)
        PHAssetCollectionTypeSmartAlbum = 2,      // 相機(jī)相冊(cè)
        PHAssetCollectionTypeMoment     = 3,      // 自動(dòng)生成的時(shí)間分組的相冊(cè)
    } PHOTOS_ENUM_AVAILABLE_IOS_TVOS(8_0, 10_0);
    typedef NS_ENUM(NSInteger, PHAssetCollectionSubtype) {
        
        // 用戶在 Photos 中創(chuàng)建的相冊(cè),也就是我所謂的邏輯相冊(cè)
        PHAssetCollectionSubtypeAlbumRegular         = 2,
        // 使用 iTunes 從 Photos 照片庫(kù)或者 iPhoto 照片庫(kù)同步過(guò)來(lái)的事件
        PHAssetCollectionSubtypeAlbumSyncedEvent     = 3,
        // 使用 iTunes 從 Photos 照片庫(kù)或者 iPhoto 照片庫(kù)同步的人物相冊(cè)。
        PHAssetCollectionSubtypeAlbumSyncedFaces     = 4,
        // 做了 AlbumSyncedEvent 應(yīng)該做的事
        PHAssetCollectionSubtypeAlbumSyncedAlbum     = 5,
        // 從相機(jī)或是外部存儲(chǔ)導(dǎo)入的相冊(cè)
        PHAssetCollectionSubtypeAlbumImported        = 6,
        
        // 用戶的 iCloud 照片流
        PHAssetCollectionSubtypeAlbumMyPhotoStream   = 100,
        // 用戶使用 iCloud 共享的相冊(cè)
        PHAssetCollectionSubtypeAlbumCloudShared     = 101,
        
        // 文檔解釋為非特殊類型的相冊(cè),主要包括從 iPhoto 同步過(guò)來(lái)的相冊(cè)
        PHAssetCollectionSubtypeSmartAlbumGeneric    = 200,
        // 相機(jī)拍攝的全景照片
        PHAssetCollectionSubtypeSmartAlbumPanoramas  = 201,
        // 相機(jī)拍攝的視頻
        PHAssetCollectionSubtypeSmartAlbumVideos     = 202,
        // 收藏文件夾
        PHAssetCollectionSubtypeSmartAlbumFavorites  = 203,
        // 延時(shí)視頻文件夾,同時(shí)也會(huì)出現(xiàn)在視頻文件夾中
        PHAssetCollectionSubtypeSmartAlbumTimelapses = 204,
        // 包含隱藏照片或視頻的文件夾
        PHAssetCollectionSubtypeSmartAlbumAllHidden  = 205,
        // 相機(jī)近期拍攝的照片或視頻
        PHAssetCollectionSubtypeSmartAlbumRecentlyAdded = 206,
        // 連拍模式拍攝的照片
        PHAssetCollectionSubtypeSmartAlbumBursts     = 207,
        // 高速攝影慢動(dòng)作解析
        PHAssetCollectionSubtypeSmartAlbumSlomoVideos = 208,
        // 相機(jī)相冊(cè),所有相機(jī)拍攝的照片或視頻都會(huì)出現(xiàn)在該相冊(cè)中
        PHAssetCollectionSubtypeSmartAlbumUserLibrary = 209,
        // 前置攝像頭所拍攝的所有照片和視頻
        PHAssetCollectionSubtypeSmartAlbumSelfPortraits PHOTOS_AVAILABLE_IOS_TVOS(9_0, 10_0) = 210,
        // 所有的截圖
        PHAssetCollectionSubtypeSmartAlbumScreenshots PHOTOS_AVAILABLE_IOS_TVOS(9_0, 10_0) = 211,
        // 在可兼容的設(shè)備上使用景深攝像模式拍的照片
        PHAssetCollectionSubtypeSmartAlbumDepthEffect PHOTOS_AVAILABLE_IOS_TVOS(10_2, 10_1) = 212,
        // 包含所有的Live Photo資源
        PHAssetCollectionSubtypeSmartAlbumLivePhotos PHOTOS_AVAILABLE_IOS_TVOS(10_3, 10_2) = 213,
        PHAssetCollectionSubtypeSmartAlbumAnimated PHOTOS_AVAILABLE_IOS_TVOS(11_0, 11_0) = 214,
        PHAssetCollectionSubtypeSmartAlbumLongExposures PHOTOS_AVAILABLE_IOS_TVOS(11_0, 11_0) = 215,
        
        //包含所有類型
        PHAssetCollectionSubtypeAny = NSIntegerMax
    } PHOTOS_ENUM_AVAILABLE_IOS_TVOS(8_0, 10_0);
  • cell展示、賦值
- (void)refreshWithPHAssetCollection:(PHAssetCollection *)collection {
    // 按時(shí)間生序
    PHFetchOptions *option = [[PHFetchOptions alloc] init];
    option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
    
    /*
     *  獲取相冊(cè)實(shí)體
     *
     *  根據(jù)實(shí)體拿到展示的三個(gè)屬性:標(biāo)題、圖片個(gè)數(shù)、相冊(cè)的第一張圖片
     */
    PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:collection options:option];
    self.textLabel.text = collection.localizedTitle;
    self.detailTextLabel.text = [NSString stringWithFormat:@"%ld",[result count]];
    [result enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        PHAsset *asset = (PHAsset *)obj;

        PHImageRequestOptions *imageOption = [[PHImageRequestOptions alloc] init];
        imageOption.resizeMode = PHImageRequestOptionsResizeModeFast;

        /*
         * 坑一 networkAccessAllowed
         
         * 是否允許網(wǎng)絡(luò)訪問(wèn),默認(rèn)為NO
         * 主要只是否可以從iCloud中下載圖像,如果iPhone開(kāi)啟iCloud優(yōu)化話存儲(chǔ)空間,設(shè)置為NO是拿不到圖片的,這里也只一個(gè)坑,需要注意一下
         */
        imageOption.networkAccessAllowed = YES;

        /*
         * 坑二 synchronous
         
         * 是否同步處理一個(gè)圖像請(qǐng)求,默認(rèn)是NO
         * 這里一般設(shè)置為NO,requestImageForAsset 請(qǐng)求就會(huì)有兩次回調(diào)。第一次返回一個(gè)低質(zhì)量的圖片(縮略圖),用于占位顯示;第二次返回的是一個(gè)高質(zhì)量的圖(原圖)
         * 如果設(shè)置為YES,請(qǐng)求就只有一次的回調(diào),返回一個(gè)高質(zhì)量的圖(原圖),會(huì)有卡頓現(xiàn)象(線程阻塞)
         */
        imageOption.synchronous = NO;

        [[PHImageManager defaultManager] requestImageForAsset:asset
                                                   targetSize:CGSizeMake(100.0f, 100.0f)
                                                  contentMode:PHImageContentModeAspectFit
                                                      options:imageOption
                                                resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
            self.imageView.image = result;
        }];
        *stop = YES;
    }];
}
  • 相冊(cè)列表結(jié)果展示
demo_相冊(cè)列表.PNG

1.2、UICollectionView

在上述表格中我們?cè)榻B過(guò)PHFetchOption,它表示一個(gè)相冊(cè)集合。
獲取collectionView的數(shù)據(jù)源其實(shí)很簡(jiǎn)單,與上述我們?cè)赾ell中獲取圖片封面一樣,通過(guò)PHAssetCollection就能拿到。

    // 照片按照時(shí)間生序排列
    PHFetchOptions *option = [[PHFetchOptions alloc] init];
    option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
    
    // 獲取照片結(jié)果集合,將其添加至數(shù)據(jù)源數(shù)組
    PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:self.colletion options:option];
    [result enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        PHAsset *asset = (PHAsset *)obj;
        [self.assets addObject:asset];
        if (idx == result.count - 1) {
            [self.collectionView reloadData];
        }
    }];


AssetsLibrary

2.1、TableView

  • 獲取相冊(cè)集合
/** 代表整個(gè)設(shè)備中的資源庫(kù)(照片庫(kù) */
@property (nonatomic, retain) ALAssetsLibrary *assetsLibrary;

// 實(shí)例化一個(gè)對(duì)象
self.assetsLibrary = [[ALAssetsLibrary alloc] init];

// 獲取相冊(cè)
[self.assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
    if (group) {
        // 將相冊(cè)圖片張數(shù)不為零的添加到數(shù)據(jù)源中
        if (group.numberOfAssets > 0) {
            [_groups addObject:group];
        }
    } else {
        [self.tableView reloadData];
    }
} failureBlock:^(NSError *error) {    
}];
  • 更新cell
- (void)refresh:(ALAssetsGroup *)assetsGroup {
    // 相冊(cè)封面賦值
    size_t height = CGImageGetHeight(assetsGroup.posterImage);
    float scale = height / 60.0f;
    UIImage *groupImage = [UIImage imageWithCGImage:assetsGroup.posterImage
                                              scale:scale
                                        orientation:UIImageOrientationUp];
    self.imageView.image = groupImage;
    
    /*
     *  property
     *
     *  ALAssetsGroupPropertyName           相冊(cè)的名字
     *  ALAssetsGroupPropertyType           相冊(cè)的類型
     *  ALAssetsGroupPropertyPersistentID   相冊(cè)的存儲(chǔ)id
     *  ALAssetsGroupPropertyURL            相冊(cè)存儲(chǔ)的位置地址
     */
    self.textLabel.text = [assetsGroup valueForProperty:ALAssetsGroupPropertyName];
    
    // 相冊(cè)圖片張數(shù)
    self.detailTextLabel.text = [NSString stringWithFormat:@"%ld", (long)[assetsGroup numberOfAssets]];
    self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
  • 信息對(duì)照表
屬性 描述
AssetsLibrary 代表整個(gè)設(shè)備中的資源庫(kù)(照片庫(kù)),通過(guò) AssetsLibrary 可以獲取照片和視頻
ALAssetsGroup 通過(guò) ALAssetsGroup 可以獲取某個(gè)相冊(cè)的信息,相冊(cè)下的資源,同時(shí)也可以對(duì)某個(gè)相冊(cè)添加資源
ALAsset 映射照片庫(kù)中的一個(gè)照片或視頻,通過(guò) ALAsset 可以獲取某個(gè)照片或視頻的詳細(xì)信息,或者保存照片和視頻
ALAssetRepresentation 對(duì) ALAsset 的封裝(但不是其子類),可以更方便地獲取 ALAsset 中的資源信息
  • 枚舉類型
    enum {
        ALAssetsGroupLibrary       // 本地和 iTunes
        ALAssetsGroupAlbum         // 從iTunes同步來(lái)的照片,不包括共享的(例如從各個(gè)軟件中保存下來(lái)的圖片)
        ALAssetsGroupEvent         // 同步到 iTunes 的(包括相機(jī)導(dǎo)入的)
        ALAssetsGroupFaces         // 同步 iTunes 的
        ALAssetsGroupSavedPhotos   // 相機(jī)膠卷
        ALAssetsGroupPhotoStream   // 照片流
        ALAssetsGroupAll           // 全部相冊(cè)
    };
  • collectionView
    /*
     *  將相冊(cè)中的圖片放入collectionView數(shù)據(jù)源中
     *
     *  NSEnumerationConcurrent  正序遍歷
     *  NSEnumerationReverse     反向遍歷
     */
    [self.assetsGroup enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
        if (result) {
            if ([[result valueForProperty:ALAssetPropertyType] isEqual:ALAssetTypePhoto]) {
                //只訪問(wèn)照片,不訪問(wèn)視頻
                [self.assets addObject:result];
            }
        } else {
            [self.collectionView reloadData];
        }
    }];

后續(xù)還會(huì)整理一下拍照和視頻錄制的一些內(nèi)容,歡迎點(diǎn)贊

參考地址:
iOS 開(kāi)發(fā)之照片框架詳解
Photos 框架實(shí)踐以及坑

最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 用到的組件 1、通過(guò)CocoaPods安裝 2、第三方類庫(kù)安裝 3、第三方服務(wù) 友盟社會(huì)化分享組件 友盟用戶反饋 ...
    SunnyLeong閱讀 15,147評(píng)論 1 180
  • 這幾天聽(tīng)說(shuō)這樣一個(gè)邏輯推理題給大家分享一下。 應(yīng)用演繹法總結(jié)自己這一生。人生的終點(diǎn)是死亡。如果我們從這...
    默默地走下去閱讀 336評(píng)論 0 1
  • 很多時(shí)候,我有很多念頭,很多時(shí)候我有很多想法,很多時(shí)候我有很多情緒,然后又是很多時(shí)候,我讓這些東西流逝了,有時(shí)候忘...
    lewis心理咨詢師閱讀 379評(píng)論 0 0
  • 交互設(shè)計(jì)研究的是怎樣在既定的環(huán)境中,幫助用戶通過(guò)一系列行為、任務(wù),良好的達(dá)成某項(xiàng)目標(biāo)。一個(gè)完整的交互系統(tǒng)必須要考慮...
    hiMute閱讀 888評(píng)論 0 1

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