一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

iOS 開(kāi)發(fā)之照片框架詳解 | Kayo's Melody

 睜開(kāi)眼就變帥 2017-02-20

一. 概要

在 iOS 設(shè)備中,照片和視頻是相當(dāng)重要的一部分。最近剛好在制作一個(gè)自定義的 iOS 圖片選擇器,順便整理一下 iOS 中對(duì)照片框架的使用方法。在 iOS 8 出現(xiàn)之前,開(kāi)發(fā)者只能使用 AssetsLibrary 框架來(lái)訪(fǎng)問(wèn)設(shè)備的照片庫(kù),這是一個(gè)有點(diǎn)跟不上 iOS 應(yīng)用發(fā)展步伐以及代碼設(shè)計(jì)原則但確實(shí)強(qiáng)大的框架,考慮到 iOS7 仍占有不少的滲透率,因此 AssetsLibrary 也是本文重點(diǎn)介紹的部分。而在 iOS8 出現(xiàn)之后,蘋(píng)果提供了一個(gè)名為 PhotoKit 的框架,一個(gè)可以讓?xiě)?yīng)用更好地與設(shè)備照片庫(kù)對(duì)接的框架,文末也會(huì)介紹一下這個(gè)框架。

另外值得強(qiáng)調(diào)的是,在 iOS 中,照片庫(kù)并不只是照片的集合,同時(shí)也包含了視頻。在 AssetsLibrary 中兩者都有相同類(lèi)型的對(duì)象去描述,只是類(lèi)型不同而已。文中為了方便,大部分時(shí)候會(huì)使用「資源」代表 iOS 中的「照片和視頻」。

二. AssetsLibrary 組成介紹

AssetsLibrary 的組成比較符合照片庫(kù)本身的組成,照片庫(kù)中的完整照片庫(kù)對(duì)象、相冊(cè)、相片都能在 AssetsLibrary 中找到一一對(duì)應(yīng)的組成,這使到 AssetsLibrary 的使用變得直觀而方便。

  • AssetsLibrary: 代表整個(gè)設(shè)備中的資源庫(kù)(照片庫(kù)),通過(guò) AssetsLibrary 可以獲取和包括設(shè)備中的照片和視頻
  • ALAssetsGroup: 映射照片庫(kù)中的一個(gè)相冊(cè),通過(guò) ALAssetsGroup 可以獲取某個(gè)相冊(cè)的信息,相冊(cè)下的資源,同時(shí)也可以對(duì)某個(gè)相冊(cè)添加資源。
  • ALAsset: 映射照片庫(kù)中的一個(gè)照片或視頻,通過(guò) ALAsset 可以獲取某個(gè)照片或視頻的詳細(xì)信息,或者保存照片和視頻。
  • ALAssetRepresentation: ALAssetRepresentation 是對(duì) ALAsset 的封裝(但不是其子類(lèi)),可以更方便地獲取 ALAsset 中的資源信息,每個(gè) ALAsset 都有至少有一個(gè) ALAssetRepresentation 對(duì)象,可以通過(guò) defaultRepresentation 獲取。而例如使用系統(tǒng)相機(jī)應(yīng)用拍攝的 RAW + JPEG 照片,則會(huì)有兩個(gè) ALAssetRepresentation,一個(gè)封裝了照片的 RAW 信息,另一個(gè)則封裝了照片的 JPEG 信息。

三. AssetsLibrary 的基本使用

AssetsLibrary 的功能很多,基本可以分為對(duì)資源的獲取/保存兩個(gè)部分,保存的部分相對(duì)簡(jiǎn)單,API 也比較少,因此這里不作詳細(xì)介紹。獲取資源的 API 則比較豐富了,一個(gè)常見(jiàn)的使用大量 AssetsLibrary API 的例子就是圖片選擇器(ALAsset Picker)。要制作一個(gè)圖片選擇器,思路應(yīng)該是獲取照片庫(kù)-列出所有相冊(cè)-展示相冊(cè)中的所有圖片-預(yù)覽圖片大圖。

首先是要檢查 App 是否有照片操作授權(quán):

1
2
3
4
5
6
7
8
9
10
NSString *tipTextWhenNoPhotosAuthorization; // 提示語(yǔ)
// 獲取當(dāng)前應(yīng)用對(duì)照片的訪(fǎng)問(wèn)授權(quán)狀態(tài)
ALAuthorizationStatus authorizationStatus = [ALAssetsLibrary authorizationStatus];
// 如果沒(méi)有獲取訪(fǎng)問(wèn)授權(quán),或者訪(fǎng)問(wèn)授權(quán)狀態(tài)已經(jīng)被明確禁止,則顯示提示語(yǔ),引導(dǎo)用戶(hù)開(kāi)啟授權(quán)
if (authorizationStatus == ALAuthorizationStatusRestricted || authorizationStatus == ALAuthorizationStatusDenied) {
    NSDictionary *mainInfoDictionary = [[NSBundle mainBundle] infoDictionary];
    NSString *appName = [mainInfoDictionary objectForKey:@"CFBundleDisplayName"];
    tipTextWhenNoPhotosAuthorization = [NSString stringWithFormat:@"請(qǐng)?jiān)谠O(shè)備的\"設(shè)置-隱私-照片\"選項(xiàng)中,允許%@訪(fǎng)問(wèn)你的手機(jī)相冊(cè)", appName];
    // 展示提示語(yǔ)
}

如果已經(jīng)獲取授權(quán),則可以獲取相冊(cè)列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
_assetsLibrary = [[ALAssetsLibrary alloc] init];
_albumsArray = [[NSMutableArray alloc] init];
[_assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
    if (group) {
        [group setAssetsFilter:[ALAssetsFilter allPhotos]];
        if (group.numberOfAssets > 0) {
            // 把相冊(cè)儲(chǔ)存到數(shù)組中,方便后面展示相冊(cè)時(shí)使用
            [_albumsArray addObject:group];
        }
    } else {
        if ([_albumsArray count] > 0) {
            // 把所有的相冊(cè)儲(chǔ)存完畢,可以展示相冊(cè)列表
        } else {
            // 沒(méi)有任何有資源的相冊(cè),輸出提示
        }
    }
} failureBlock:^(NSError *error) {
    NSLog(@"Asset group not found!\n");
}];

上面的代碼中,遍歷出所有的相冊(cè)列表,并把相冊(cè)中資源數(shù)不為空的相冊(cè) ALAssetGroup 對(duì)象的引用儲(chǔ)存到一個(gè)數(shù)組中。這里需要強(qiáng)調(diào)幾點(diǎn):

  • iOS 中允許相冊(cè)為空,即相冊(cè)中沒(méi)有任何資源,如果不希望獲取空相冊(cè),則需要像上面的代碼中那樣手動(dòng)過(guò)濾
  • ALAssetsGroup 有一個(gè) setAssetsFilter 的方法,可以傳入一個(gè)過(guò)濾器,控制只獲取相冊(cè)中的照片或只獲取視頻。一旦設(shè)置過(guò)濾,ALAssetsGroup 中資源列表和資源數(shù)量的獲取也會(huì)被自動(dòng)更新。
  • 整個(gè) AssetsLibrary 中對(duì)相冊(cè)、資源的獲取和保存都是使用異步處理(Asynchronous),這是考慮到資源文件體積相當(dāng)比較大(還可能很大)。例如上面的遍歷相冊(cè)操作,相冊(cè)的結(jié)果使用 block 輸出,如果相冊(cè)遍歷完畢,則最后一次輸出的 block 中的 group 參數(shù)值為 nil。而 stop 參數(shù)則是用于手工停止遍歷,只要把 *stop 置 YES,則會(huì)停止下一次的遍歷。關(guān)于這一點(diǎn)常常會(huì)引起誤會(huì),所以需要注意。

現(xiàn)在,已經(jīng)可以獲取相冊(cè)了,接下來(lái)是獲取相冊(cè)中的資源:

1
2
3
4
5
6
7
8
_imagesAssetArray = [[NSMutableArray alloc] init];
[assetsGroup enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
    if (result) {
        [_imagesAssetArray addObject:result];
    } else {
        // result 為 nil,即遍歷相片或視頻完畢,可以展示資源列表
    }
}];

跟遍歷相冊(cè)的過(guò)程類(lèi)似,遍歷相片也是使用一系列的異步方法,其中上面的方法所輸出的 block 中,除了 result 參數(shù)表示資源信息,stop 用于手工停止遍歷外,還提供了一個(gè) index 參數(shù),這個(gè)參數(shù)表示資源的索引。一般來(lái)說(shuō),展示資源列表都會(huì)使用縮略圖(result.thumbnail),因此即使資源很多,遍歷資源的速度也會(huì)相當(dāng)快。但如果確實(shí)需要加載資源的高清圖或者其他耗時(shí)的處理,則可以利用上面的 index 參數(shù)和 stop 參數(shù)做一個(gè)分段拉取資源。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
NSUInteger _targetIndex; // index 目標(biāo)值,拉取資源直到這個(gè)值就手工停止拉取
NSUInteger _currentIndex; // 當(dāng)前 index,每次拉取資源時(shí)從這個(gè)值開(kāi)始
_targetIndex = 50;
_currentIndex = 0;
- (void)loadAssetWithAssetsGroup:(assetsGroup *)assetsGroup {
    [assetsGroup enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:_currentIndex] options:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
        _currentIndex = index;
        if (index > _targetIndex) {
            // 拉取資源的索引如果比目標(biāo)值大,則停止拉取
            *stop = YES;
        } else {
            if (result) {
                [_imagesAssetArray addObject:result];
            } else {
                // result 為 nil,即遍歷相片或視頻完畢
            }
        }
    }];
}
// 之前拉取的數(shù)據(jù)已經(jīng)顯示完畢,需要展示新數(shù)據(jù),重新調(diào)用 loadAssetWithAssetsGroup 方法,并根據(jù)需要更新 _targetIndex 的值

最后一步是獲取圖片詳細(xì)信息,例如:

1
2
3
4
// 獲取資源圖片的詳細(xì)資源信息,其中 imageAsset 是某個(gè)資源的 ALAsset 對(duì)象
ALAssetRepresentation *representation = [imageAsset defaultRepresentation];
// 獲取資源圖片的 fullScreenImage
UIImage *contentImage = [UIImage imageWithCGImage:[representation fullScreenImage]];

對(duì)于一個(gè) ALAssetRepresentation,里面包含了圖片的多個(gè)版本。最常用的是 fullResolutionImage 和 fullScreenImage。fullResolutionImage 是圖片的原圖,通過(guò) fullResolutionImage 獲取的圖片沒(méi)有任何處理,包括通過(guò)系統(tǒng)相冊(cè)中“編輯”功能處理后的信息也沒(méi)有被包含其中,因此需要展示“編輯”功能處理后的信息,使用 fullResolutionImage 就比較不方便,另外 fullResolutionImage 的拉取也會(huì)比較慢,在多張 fullResolutionImage 中切換時(shí)能明顯感覺(jué)到圖片的加載過(guò)程。因此這里建議獲取圖片的 fullScreenImage,它是圖片的全屏圖版本,這個(gè)版本包含了通過(guò)系統(tǒng)相冊(cè)中“編輯”功能處理后的信息,同時(shí)也是一張縮略圖,但圖片的失真很少,缺點(diǎn)是圖片的尺寸是一個(gè)適應(yīng)屏幕大小的版本,因此展示圖片時(shí)需要作出額外處理,但考慮到加載速度非??斓脑颍ㄔ诙鄰垐D片之間切換感受不到圖片加載耗時(shí)),仍建議使用 fullScreenImage。

系統(tǒng)相冊(cè)的處理過(guò)程大概也是如上,可以看出,在整個(gè)過(guò)程中并沒(méi)有使用到圖片的 fullResolutionImage,從相冊(cè)列表展示到最終查看資源,都是使用縮略圖,這也是 iOS 相冊(cè)加載快的一個(gè)重要原因。

三. AssetsLibrary 的坑點(diǎn)

作為一套老框架,AssetsLibrary 不但有坑,而且還不少,除了上面提到的資源異步拉取時(shí)需要注意的事項(xiàng),下面幾點(diǎn)也是值得注意的:

1. AssetsLibrary 實(shí)例需要強(qiáng)引用

實(shí)例一個(gè) AssetsLibrary 后,如上面所示,我們可以通過(guò)一系列枚舉方法獲取到需要的相冊(cè)和資源,并把其儲(chǔ)存到數(shù)組中,方便用于展示。但是,當(dāng)我們把這些獲取到的相冊(cè)和資源儲(chǔ)存到數(shù)組時(shí),實(shí)際上只是在數(shù)組中儲(chǔ)存了這些相冊(cè)和資源在 AssetsLibrary 中的引用(指針),因而無(wú)論把相冊(cè)和資源儲(chǔ)存數(shù)組后如何利用這些數(shù)據(jù),都首先需要確保 AssetsLibrary 沒(méi)有被 ARC 釋放,否則把數(shù)據(jù)從數(shù)組中取出來(lái)時(shí),會(huì)發(fā)現(xiàn)對(duì)應(yīng)的引用數(shù)據(jù)已經(jīng)丟失(參見(jiàn)下圖)。這一點(diǎn)較為容易被忽略,因此建議在使用 AssetsLibrary 的 viewController 中,把 AssetsLibrary 作為一個(gè)強(qiáng)持有的 property 或私有變量,避免在枚舉出 AssetsLibrary 中所需要的數(shù)據(jù)后,AssetsLibrary 就被 ARC 釋放了。

如下圖:實(shí)例化一個(gè) AssetsLibrary 的局部變量,枚舉所有相冊(cè)并儲(chǔ)存在名為 _albumsArray 的數(shù)組中,展示相冊(cè)時(shí)再次查看數(shù)組,發(fā)現(xiàn) ALAssetsGroup 中的數(shù)據(jù)已經(jīng)丟失。

ALAssetsLibrary_release

2. AssetsLibrary 遵循寫(xiě)入優(yōu)先原則

寫(xiě)入優(yōu)先也就是說(shuō),在利用 AssetsLibrary 讀取資源的過(guò)程中,有任何其它的進(jìn)程(不一定是同一個(gè) App)在保存資源時(shí),就會(huì)收到 ALAssetsLibraryChangedNotification,讓用戶(hù)自行中斷讀取操作。最常見(jiàn)的就是讀取 fullResolutionImage 時(shí),用進(jìn)程在寫(xiě)入,由于讀取 fullResolutionImage 耗時(shí)較長(zhǎng),很容易就會(huì) exception。

3. 開(kāi)啟 Photo Stream 容易導(dǎo)致 exception

本質(zhì)上,這跟上面的 AssetsLibrary 遵循寫(xiě)入優(yōu)先原則是同一個(gè)問(wèn)題。如果用戶(hù)開(kāi)啟了共享照片流(Photo Stream),共享照片流會(huì)以 mstreamd 的方式“偷偷”執(zhí)行,當(dāng)有人把相片寫(xiě)入 Camera Roll 時(shí),它就會(huì)自動(dòng)保存到 Photo Stream Album 中,如果用戶(hù)剛好在讀取,那就跟上面說(shuō)的一樣產(chǎn)生 exception 了。由于共享照片流是用戶(hù)決定是否要開(kāi)啟的,所以開(kāi)發(fā)者無(wú)法改變,但是可以通過(guò)下面的接口在需要保護(hù)的時(shí)刻關(guān)閉監(jiān)聽(tīng)共享照片流產(chǎn)生的頻繁通知信息。

1
[ALAssetsLibrary disableSharedPhotoStreamsSupport];

四. PhotoKit 簡(jiǎn)介

PhotoKit 是一套比 AssetsLibrary 更完整也更高效的庫(kù),對(duì)資源的處理跟 AssetsLibrary 也有很大的不同。

首先簡(jiǎn)單介紹幾個(gè)概念:

  • PHAsset: 代表照片庫(kù)中的一個(gè)資源,跟 ALAsset 類(lèi)似,通過(guò) PHAsset 可以獲取和保存資源
  • PHFetchOptions: 獲取資源時(shí)的參數(shù),可以傳 nil,即使用系統(tǒng)默認(rèn)值
  • PHFetchResult: 表示一系列的資源集合,也可以是相冊(cè)的集合
  • PHAssetCollection: 表示一個(gè)相冊(cè)或者一個(gè)時(shí)刻,或者是一個(gè)「智能相冊(cè)(系統(tǒng)提供的特定的一系列相冊(cè),例如:最近刪除,視頻列表,收藏等等,如下圖所示)
  • PHImageManager: 用于處理資源的加載,加載圖片的過(guò)程帶有緩存處理,可以通過(guò)傳入一個(gè) PHImageRequestOptions 控制資源的輸出尺寸等規(guī)格
  • PHImageRequestOptions: 如上面所說(shuō),控制加載圖片時(shí)的一系列參數(shù)

下圖中 UITableView 的第二個(gè) section 就是 PhotoKit 所列出的所有智能相冊(cè)

photokit-album-list

再列出幾個(gè)代碼片段,展示如何獲取相冊(cè)以及某個(gè)相冊(cè)下資源的代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 列出所有相冊(cè)智能相冊(cè)
PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
// 列出所有用戶(hù)創(chuàng)建的相冊(cè)
PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
// 獲取所有資源的集合,并按資源的創(chuàng)建時(shí)間排序
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
// 在資源的集合中獲取第一個(gè)集合,并獲取其中的圖片
PHCachingImageManager *imageManager = [[PHCachingImageManager alloc] init];
PHAsset *asset = assetsFetchResults[0];
[imageManager requestImageForAsset:asset
                         targetSize:SomeSize
                        contentMode:PHImageContentModeAspectFill
                            options:nil
                      resultHandler:^(UIImage *result, NSDictionary *info) {
                           
                          // 得到一張 UIImage,展示到界面上
                           
                      }];

結(jié)合上面幾個(gè)代碼片段上看,PhotoKit 相對(duì) AssetsLibrary 主要有三點(diǎn)重要的改進(jìn):

  • 從 AssetsLibrary 中獲取數(shù)據(jù),無(wú)論是相冊(cè),還是資源,本質(zhì)上都是使用枚舉的方式,遍歷照片庫(kù)取得相應(yīng)的數(shù)據(jù)。而 PhotoKit 則是通過(guò)傳入?yún)?shù),直接獲取相應(yīng)的數(shù)據(jù),因而效率會(huì)提高不少。
  • 在 AssetsLibrary 中,相冊(cè)和資源是對(duì)應(yīng)不同的對(duì)象(ALAssetGroup 和 ALAsset),因此獲取相冊(cè)和獲取資源是兩個(gè)完全沒(méi)有關(guān)聯(lián)的接口。而 PhotoKit 中則有 PHFetchResult 這個(gè)可以統(tǒng)一儲(chǔ)存相冊(cè)或資源的對(duì)象,因此處理相冊(cè)和資源時(shí)也會(huì)比較方便。
  • PhotoKit 返回資源結(jié)果時(shí),同時(shí)返回了資源的元數(shù)據(jù),獲取元數(shù)據(jù)在 AssetsLibrary 中是很難辦到的一件事。同時(shí)通過(guò) PHAsset,開(kāi)發(fā)者還能直接獲取資源是否被收藏(favorite)和隱藏(hidden),拍攝圖片時(shí)是否開(kāi)啟了 HDR 或全景模式,甚至能通過(guò)一張連拍圖片獲取到連拍圖片中的其他圖片。這也是文章開(kāi)頭說(shuō)的,PhotoKit 能更好地與設(shè)備照片庫(kù)接入的一個(gè)重要因素。

關(guān)于 PhotoKit,建議可以參考 Apple 的 Example app using Photos framework

系列文章:
iOS 開(kāi)發(fā)之照片框架詳解
iOS 開(kāi)發(fā)之照片框架詳解之二 —— PhotoKit 詳解(上)
iOS 開(kāi)發(fā)之照片框架詳解之二 —— PhotoKit 詳解(下)

參考資料:
objc中國(guó) - 照片框架
Example app using Photos framework
AssetsLibrary Framework Reference

本文由 Kayo Lee 發(fā)表,本文鏈接:http:///ios-development-and-detail-of-photo-framework.html

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多

    91插插插外国一区二区| 亚洲精品国产精品日韩| 国产又猛又黄又粗又爽无遮挡| 日韩成人免费性生活视频| 日韩欧美中文字幕av| 久久亚洲成熟女人毛片| 欧美精品久久一二三区| 亚洲视频一级二级三级| 精品香蕉国产一区二区三区| 亚洲av熟女一区二区三区蜜桃| 少妇特黄av一区二区三区| 色哟哟国产精品免费视频| 亚洲熟女精品一区二区成人| 青青操日老女人的穴穴| 日韩在线欧美一区二区| 麻豆视频传媒入口在线看| 亚洲欧美中文日韩综合| 成人国产一区二区三区精品麻豆| 国产农村妇女成人精品| 五月激情五月天综合网| 日韩欧美国产精品中文字幕| 国产成人av在线免播放观看av | 白丝美女被插入视频在线观看| 在线观看欧美视频一区| 人妻巨大乳一二三区麻豆| 超薄丝袜足一区二区三区| 人体偷拍一区二区三区| 日本久久精品在线观看| 国产一级内射麻豆91| 在线日韩中文字幕一区| 香港国产三级久久精品三级| 日本精品最新字幕视频播放| 国产福利在线播放麻豆| 91免费一区二区三区| 中文字幕久热精品视频在线| 国产欧美日韩精品一区二区| 老鸭窝精彩从这里蔓延| 欧美国产极品一区二区| 国产免费成人激情视频| 暴力性生活在线免费视频| 五月婷婷亚洲综合一区|