前言
我們公司做了一個DLNA的投屏軟件,但是iOS是不能跨應用訪問數據的,所以對于局域網投屏視頻和圖片需要把圖片或者視頻寫入到應用的沙盒路徑下。
在我之前的前輩用的是AssetsLibrary,他是在進入界面之前寫入,等到完全都寫完了才會去顯示。之前拍照的照片大小不是很大,而且手機的存儲空間也不大,對于用戶來說這么處理完全是沒有問題的。但是,后來有用戶反饋說在本地媒體界面一直都有那個“菊花轉”。后來我們發現可能是用戶的本地媒體數據過于大,倒是程序假死。
我們老大說,你把這個功能優化一下,目標就是像微信那樣是最好的。后來我們就選用的PhotoKit這個框架。
研究
之前并沒有具體用過這個框架,所以就現在網上研究了一下。很幸運,我找到了一個很類似的DEMO。
我們只要在相應的控制器界面導入#import <Photos/Photos.h>
就行。我們還需要在info.plist中添加相冊的訪問權限。
之后,我們建立一個二級控制器就可以了。
初用
彎路1:
當我們添加授權之后再相應的界面就會出現這么一個彈框
當我點擊好的時候 界面并沒有數據,但是當我第二期進入界面的時候數據就顯示出來了。
解決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:
在上面獲取到的相冊的名稱,我在控制臺打出來的都是英文的
但是我們想要的并不是英文的。
解決2:
依舊是在info.plist
中,如下圖:
顯示圖片,這里用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