iOS PhotoKit 初用

c4ibD3發表於2017-06-16

前言


我們公司做了一個DLNA的投屏軟體,但是iOS是不能跨應用訪問資料的,所以對於區域網投屏視訊和圖片需要把圖片或者視訊寫入到應用的沙盒路徑下。
在我之前的前輩用的是AssetsLibrary,他是在進入介面之前寫入,等到完全都寫完了才會去顯示。之前拍照的照片大小不是很大,而且手機的儲存空間也不大,對於使用者來說這麼處理完全是沒有問題的。但是,後來有使用者反饋說在本地媒體介面一直都有那個“菊花轉”。後來我們發現可能是使用者的本地媒體資料過於大,倒是程式假死。
我們老大說,你把這個功能優化一下,目標就是像微信那樣是最好的。後來我們就選用的PhotoKit這個框架。


研究


之前並沒有具體用過這個框架,所以就現在網上研究了一下。很幸運,我找到了一個很類似的DEMO。
我們只要在相應的控制器介面匯入#import <Photos/Photos.h>就行。我們還需要在info.plist中新增相簿的訪問許可權。

iOS PhotoKit 初用
在info.plist新增相簿授權

之後,我們建立一個二級控制器就可以了。


初用


彎路1:

當我們新增授權之後再相應的介面就會出現這麼一個彈框

iOS PhotoKit 初用
相簿訪問授權

當我點選好的時候 介面並沒有資料,但是當我第二期進入介面的時候資料就顯示出來了。

解決1:

我們需要對這個彈框的點選事件進行處理,這裡我們直接上程式碼:

- (void)getAuthorized{
    //判斷是否有訪問許可權
    PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
    //還沒有去做選擇
    if (status == PHAuthorizationStatusNotDetermined) {
        [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
            //已經授權
            if (status == PHAuthorizationStatusAuthorized) {
                dispatch_async(dispatch_get_main_queue(), ^{
              //已經授權,顯示相簿 或者圖片
                });
            }else{
                //做一個沒有授權的提示
            }
        }];
    }
    //已經授權
    else if (status == PHAuthorizationStatusAuthorized){
        dispatch_async(dispatch_get_main_queue(), ^{
          //已經授權,顯示相簿 或者圖片
        });
    }
    //拒絕訪問
    else if (status == PHAuthorizationStatusRestricted){
        dispatch_async(dispatch_get_main_queue(), ^{
        //做一個沒有授權的提示
        });
    }
}複製程式碼

那麼接了下來我們繼續。
這裡我們先去做了獲取相簿的功能:

- (void)getAllAlbums{
    PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
    for (PHCollection *collection in smartAlbums) {
        if ([collection isKindOfClass:[PHCollection class]]) {
            PHAssetCollection *assetCollection = (PHAssetCollection *)collection;

            switch (assetCollection.assetCollectionSubtype) {
                case PHAssetCollectionSubtypeSmartAlbumAllHidden:
                    break;
                case PHAssetCollectionSubtypeSmartAlbumUserLibrary:{
                    PHFetchResult *assetFetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:self.options];
                    [self.smartFetchResultArray insertObject:assetFetchResult atIndex:0];
                    [self.smartFetchResultTitlt insertObject:collection.localizedTitle atIndex:0];
                }
                    break;
                default:{
                    PHFetchResult *assetFetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:self.options];
                    [self.smartFetchResultTitlt addObject:collection.localizedTitle];
                    [self.smartFetchResultArray addObject:assetFetchResult];
                }
                    break;
            }
        }
    }
}複製程式碼

這裡的self.smartFetchResultTitlt是用來儲存相簿標題的陣列;self.smartFetchResultArray是用來儲存相簿內容的;self.options需要設定一下,程式碼如下:

- (PHFetchOptions *)options {
    if (!_options) {
        _options = [[PHFetchOptions alloc] init];
        _options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
    }
    return _options;
}複製程式碼
彎路2:

在上面獲取到的相簿的名稱,我在控制檯打出來的都是英文的

iOS PhotoKit 初用
英文相簿名

但是我們想要的並不是英文的。

解決2:

依舊是在info.plist中,如下圖:

iOS PhotoKit 初用
修改屬性

顯示圖片,這裡用PhototKit自身的PHCachingImageManager做顯示就可以了。
我是用了一個collectionView做顯示。方法如下:

    [self.imageManager requestImageForAsset:asset targetSize:CGSizeMake(self.bounds.size.width, self.bounds.size.width) contentMode:PHImageContentModeAspectFit options:nil resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
        weakSelf.imageView.image = result;
    }];複製程式碼

這裡顯示的只是一個縮圖,並不是很清晰,如果放到滿屏看就會很模糊,那麼還有另一種方式去獲得清晰的圖片:

WeakSelf(weakSelf);
    [[PHImageManager defaultManager]requestImageDataForAsset:self.asset                                                             options:nil
                                               resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {

                                                   UIImage *selectedImage = [UIImage imageWithData:imageData];
                                                   weakSelf.imageView.image = selectedImage;
                                                   weakSelf.title = [[info objectForKey:@"PHImageFileURLKey"] lastPathComponent];
                                               }];複製程式碼

這樣不僅獲得了圖片,還可以獲得這個照片的名字。


上面說的都是圖片,接下來說說視訊。

我麼可以用下面這個方法去獲取視訊:

PHVideoRequestOptions *phVideoRequestOptions = [[PHVideoRequestOptions alloc]init];
        phVideoRequestOptions.version = PHImageRequestOptionsVersionCurrent;
        phVideoRequestOptions.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
        PHImageManager *manager = [PHImageManager defaultManager];
        [manager requestAVAssetForVideo:asset options:phVideoRequestOptions resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
            ShowVIdeoViewController *shouwVideoVC = [[ShowVIdeoViewController alloc]init];
            shouwVideoVC.asset = asset;
            shouwVideoVC.fileName =[[info objectForKey:@"PHImageFileSandboxExtensionTokenKey"] lastPathComponent];
            [weakSelf.navigationController pushViewController:shouwVideoVC animated:YES];
        }];複製程式碼
彎路3:

就是這個方法的等待時間比較長,使用者體驗不太好。


Live Photo

這個功能出現了有一段時間,我知道的軟體只有微博支援了Live Photo的功能(小人可能見識短淺,有盆友知到別的軟體也支援的話可以 私聊告訴我),我看PhotoKit支援Live Photo,那麼我就小小的研究了一下。
但是在這個過程中我也是遇到了一些問題的,
我們看原始碼中給本地媒體分了很多種型別:

typedef NS_ENUM(NSInteger, PHAssetMediaType) {
    PHAssetMediaTypeUnknown = 0,
    PHAssetMediaTypeImage   = 1,
    PHAssetMediaTypeVideo   = 2,
    PHAssetMediaTypeAudio   = 3,
} PHOTOS_ENUM_AVAILABLE_IOS_TVOS(8_0, 10_0);

typedef NS_OPTIONS(NSUInteger, PHAssetMediaSubtype) {
    PHAssetMediaSubtypeNone               = 0,

    // Photo subtypes
    PHAssetMediaSubtypePhotoPanorama      = (1UL << 0),
    PHAssetMediaSubtypePhotoHDR           = (1UL << 1),
    PHAssetMediaSubtypePhotoScreenshot PHOTOS_AVAILABLE_IOS_TVOS(9_0, 10_0) = (1UL << 2),
    PHAssetMediaSubtypePhotoLive PHOTOS_AVAILABLE_IOS_TVOS(9_1, 10_0) = (1UL << 3),
    PHAssetMediaSubtypePhotoDepthEffect PHOTOS_AVAILABLE_IOS_TVOS(10_2, 10_1) = (1UL << 4),

    // Video subtypes
    PHAssetMediaSubtypeVideoStreamed      = (1UL << 16),
    PHAssetMediaSubtypeVideoHighFrameRate = (1UL << 17),
    PHAssetMediaSubtypeVideoTimelapse     = (1UL << 18),
} PHOTOS_AVAILABLE_IOS_TVOS(8_0, 10_0);複製程式碼
彎路4:

首先LivePhoto應該是屬於PHAssetMediaTypeImage的,然後是屬於PHAssetMediaSubtypePhotoLive的。
正常的理解就是這樣對不對,但是在我的LivePhoto的相簿中有的竟然會識別不出來。我去了本地媒體相簿看了一下識別不出來的那些照片的型別,左上角竟然有兩個標識,一個是Live,另一個是HDR
後來我就是把所有的型別都列印了一下,我發現這樣的相片不屬於任何一個型別。

解決4:

我在控制器打了一下看了一下子媒體型別的值,LivePhoto的型別值是8,既是LivePhoto又是HDR的型別值是10。我之前的媒體型別判斷是:

asset.mediaSubtypes == PHAssetMediaSubtypePhotoLive複製程式碼

而改成:

asset.mediaSubtypes >= PHAssetMediaSubtypePhotoLive複製程式碼

就可以了


下面來說我們怎麼顯示Live Photo:

PHLivePhotoRequestOptions *options = [[PHLivePhotoRequestOptions alloc]init];
    options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
    options.networkAccessAllowed = YES;
    options.progressHandler = ^(double progress, NSError * _Nullable error, BOOL * _Nonnull stop, NSDictionary * _Nullable info) {
        NSLog(@"progress = %f",progress);
    };
    [[PHImageManager defaultManager]requestLivePhotoForAsset:self.asset targetSize:self.livePhotoView.bounds.size contentMode:PHImageContentModeAspectFill options:options resultHandler:^(PHLivePhoto * _Nullable livePhoto, NSDictionary * _Nullable info) {
        self.livePhotoView.livePhoto = livePhoto;
        NSLog(@"info = %@",info);
    }];複製程式碼

這裡的self.livephotoview 是PHLivePhotoView這個類 ,就和普通的View初始化一樣。
這樣你現實出來的LivePhoto就可以了,這時你只要按住照片,照片就會動起來,這裡你也可以設定你的播放設定,我是這樣設定的:

 [self.livePhotoView startPlaybackWithStyle:PHLivePhotoViewPlaybackStyleHint];複製程式碼

這樣設定的效果就是進來介面,livePhoto就會自動播放一次。

彎路5:

我先通過上面的方法列印info裡面的資訊,但是控制檯給我這樣的資訊:

error reading settings archive file: <ISRootSettings: /var/mobile/Containers/Data/Application/BCAA7EBA-543E-4B9E-B945-D8C4C509C491/Documents/com.C4ibD3.PhotoKitDemo.settings/ISRootSettings_10.plist>
2017-06-16 09:00:16.433082+0800 PhotoKitDemo[1444:307243] info = {
}複製程式碼

不知道是為什麼?


傳送門

github:PhotoKitDemo

相關文章