IOS - 照片/視頻選擇器

一個好用的照片選擇器
github地址:https://github.com/SilenceLove/HXPhotoPicker
照片選擇器 支持 ios8 以上

IMG_4311.PNG

IMG_4308.PNG

IMG_4309.PNG

目錄

特性 - Features

  • √ 查看/選擇GIF圖片
  • √ 照片、視頻可同時多選/原圖
  • √ 3DTouch預覽照片
  • √ 長按拖動改變順序
  • √ 自定義相機拍照/錄制視頻
  • √ 自定義轉場動畫
  • √ 查看/選擇LivePhoto IOS9.1以上才有用
  • √ 支持瀏覽網絡圖片
  • √ 支持自定義裁剪圖片
  • √ 觀察系統相冊變化實時增刪
  • √ 支持傳入本地圖片
  • √ 支持在線下載iCloud上的資源
  • √ 兩種相冊展現方式(列表、彈窗)
  • √ 支持Cell上添加
  • √ 同一界面多個不同選擇器

安裝 - Installation

  • Cocoapods:pod 'HXPhotoPicker', '~> 2.2.5'搜索不到庫或最新版請執行pod repo update
  • 手動導入:將項目中的“HXPhotoPicker”文件夾拖入項目中
  • 網絡圖片加載使用的是SDWebImage v4.4.1 || YYWebImage v1.0.5
  • 如果想要加載網絡gif圖片請使用YYWebImage
  • 使用前導入頭文件 "HXPhotoPicker.h"

要求 - Requirements

  • iOS8及以上系統可使用. ARC環境. - iOS 8 or later. Requires ARC
  • 在Xcode8環境下將項目運行在iOS10的設備/模擬器中,訪問相冊和相機需要配置三個info.plist文件
  • Privacy - Photo Library Usage Description 和 Privacy - Camera Usage Description 以及 Privacy - Microphone Usage Description
  • 相機拍照功能請使用真機調試

示例 - Examples

如何獲取照片和視頻

根據選擇完成后返回的 HXPhotoModel 對象獲取

// 獲取 image
// 如果為網絡圖片的話會先下載
// size 代表獲取image的質量
// PHImageManagerMaximumSize 獲取原圖
[photoModel requestPreviewImageWithSize:PHImageManagerMaximumSize startRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel *model) {
    // 如果照片在iCloud上會去下載,此回調代表開始下載iCloud上的照片
    // 如果照片在本地存在此回調則不會走
} progressHandler:^(double progress, HXPhotoModel *model) {
    // iCloud下載進度
    // 如果為網絡圖片,則是網絡圖片的下載進度
} success:^(UIImage *image, HXPhotoModel *model, NSDictionary *info) {
    // 獲取成功
} failed:^(NSDictionary *info, HXPhotoModel *model) {
    // 獲取失敗
}];

// 獲取 imageData
// 如果為網絡圖片的話會先下載
[photoModel requestImageDataStartRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel *model) {
    // 開始下載iCloud上照片的imageData
} progressHandler:^(double progress, HXPhotoModel *model) {
    // iCloud下載進度
} success:^(NSData *imageData, UIImageOrientation orientation, HXPhotoModel *model, NSDictionary *info) {
    // 獲取成功
} failed:^(NSDictionary *info, HXPhotoModel *model) {
    // 獲取失敗
}];

// 獲取視頻的 AVAsset
[photoModel requestAVAssetStartRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel *model) {
    // 開始下載iCloud上的視頻
} progressHandler:^(double progress, HXPhotoModel *model) {
    // iCloud下載進度
} success:^(AVAsset *avAsset, AVAudioMix *audioMix, HXPhotoModel *model, NSDictionary *info) {
    // 獲取成功
} failed:^(NSDictionary *info, HXPhotoModel *model) {
    // 獲取失敗
}];

// 獲取 LivePhoto 
// PHImageManagerMaximumSize代表原圖
[photoModel requestLivePhotoWithSize:PHImageManagerMaximumSize startRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel *model) {
    // 開始下載iCloud上的 LivePhoto
} progressHandler:^(double progress, HXPhotoModel *model) {
    // iCloud下載進度
} success:^(PHLivePhoto *livePhoto, HXPhotoModel *model, NSDictionary *info) {
    // 獲取成功
} failed:^(NSDictionary *info, HXPhotoModel *model) {
    // 獲取失敗
}];

// 導出視頻地址 
// presetName 視頻導出的質量
[photoModel exportVideoWithPresetName:AVAssetExportPresetHighestQuality startRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel *model) {
    // 開始下載iCloud上的視頻
} iCloudProgressHandler:^(double progress, HXPhotoModel *model) {
    // iCloud下載進度
} exportProgressHandler:^(float progress, HXPhotoModel *model) {
    // 視頻導出進度
} success:^(NSURL *videoURL, HXPhotoModel *model) {
    // 導出成功
} failed:^(NSDictionary *info, HXPhotoModel *model) {
    // 導出失敗
}];

NSArray+HXExtension
/**
獲取image
如果model是視頻的話,獲取的則是視頻封面

@param original 是否原圖
@param completion imageArray 獲取成功的image數組, errorArray 獲取失敗的model數組
*/
- (void)hx_requestImageWithOriginal:(BOOL)original completion:(void (^)(NSArray<UIImage *> * _Nullable imageArray, NSArray<HXPhotoModel *> * _Nullable errorArray))completion;

/**
獲取imageData

@param completion 完成回調,獲取失敗的不會添加到數組中
*/
- (void)hx_requestImageDataWithCompletion:(void (^)(NSArray<NSData *> * _Nullable imageDataArray))completion;

/**
獲取AVAsset

@param completion 完成回調,獲取失敗的不會添加到數組中
*/
- (void)hx_requestAVAssetWithCompletion:(void (^)(NSArray<AVAsset *> * _Nullable assetArray))completion;

/**
獲取視頻地址

@param presetName AVAssetExportPresetHighestQuality / AVAssetExportPresetMediumQuality
@param completion 完成回調,獲取失敗的不會添加到數組中
*/
- (void)hx_requestVideoURLWithPresetName:(NSString *)presetName completion:(void (^)(NSArray<NSURL *> * _Nullable videoURLArray))completion;

跳轉相冊選擇照片

// 懶加載 照片管理類
- (HXPhotoManager *)manager {
    if (!_manager) {
        _manager = [[HXPhotoManager alloc] initWithType:HXPhotoManagerSelectedTypePhotoAndVideo];
    }
    return _manager;
}

// 一個方法調用
HXWeakSelf
[self hx_presentSelectPhotoControllerWithManager:self.manager didDone:^(NSArray<HXPhotoModel *> *allList, NSArray<HXPhotoModel *> *photoList, NSArray<HXPhotoModel *> *videoList, BOOL isOriginal, UIViewController *viewController, HXPhotoManager *manager) {
    weakSelf.total.text = [NSString stringWithFormat:@"總數量:%ld   ( 照片:%ld   視頻:%ld )",allList.count, photoList.count, videoList.count];
    weakSelf.original.text = isOriginal ? @"YES" : @"NO";
    NSSLog(@"block - all - %@",allList);
    NSSLog(@"block - photo - %@",photoList);
    NSSLog(@"block - video - %@",videoList);
} cancel:^(UIViewController *viewController, HXPhotoManager *manager) {
    NSSLog(@"block - 取消了");
}];

// 照片選擇控制器 
HXCustomNavigationController *nav = [[HXCustomNavigationController alloc] initWithManager:self.manager delegate:self];
[self presentViewController:nav animated:YES completion:nil];

// 通過 HXCustomNavigationControllerDelegate 代理返回選擇的圖片以及視頻
/**
點擊完成按鈕

@param photoNavigationViewController self
@param allList 已選的所有列表(包含照片、視頻)
@param photoList 已選的照片列表
@param videoList 已選的視頻列表
@param original 是否原圖
*/
- (void)photoNavigationViewController:(HXCustomNavigationController *)photoNavigationViewController didDoneAllList:(NSArray<HXPhotoModel *> *)allList photos:(NSArray<HXPhotoModel *> *)photoList videos:(NSArray<HXPhotoModel *> *)videoList original:(BOOL)original;

/**
點擊取消

@param photoNavigationViewController self
*/
- (void)photoNavigationViewControllerDidCancel:(HXCustomNavigationController *)photoNavigationViewController;

使用HXPhotoView布局

// 懶加載 照片管理類
- (HXPhotoManager *)manager {
    if (!_manager) {
        _manager = [[HXPhotoManager alloc] initWithType:HXPhotoManagerSelectedTypePhotoAndVideo];
    }
    return _manager;
}  
HXPhotoView *photoView = [[HXPhotoView alloc] initWithFrame:CGRectMake((414 - 375) / 2, 100, 375, 400) manager:self.manager];
photoView.delegate = self;
photoView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:photoView];

// 代理返回 選擇、移動順序、刪除之后的圖片以及視頻
- (void)photoView:(HXPhotoView *)photoView changeComplete:(NSArray<HXPhotoModel *> *)allList photos:(NSArray<HXPhotoModel *> *)photos videos:(NSArray<HXPhotoModel *> *)videos original:(BOOL)isOriginal;

// 當view更新高度時調用
- (void)photoView:(HXPhotoView *)photoView updateFrame:(CGRect)frame;

// 刪除網絡圖片的地址
- (void)photoView:(HXPhotoView *)photoView deleteNetworkPhoto:(NSString *)networkPhotoUrl;

具體請查看HXPhotoView.h
...

如何保存草稿

通過 HXPhotoManager 對象進行存儲
/**
保存模型數組到本地

@param success 成功
@param failed 失敗
*/
- (void)saveSelectModelArraySuccess:(void (^)(void))success failed:(void (^)(void))failed;
/**
刪除本地保存的模型數組

@return success or failed
*/
- (BOOL)deleteLocalSelectModelArray;
/**
獲取保存在本地的模型數組

*/
- (void)getSelectedModelArrayComplete:(void (^)(NSArray<HXPhotoModel *> *modelArray))complete;

// 保存草稿
[self.manager saveSelectModelArraySuccess:^{
    // 保存草稿成功
} failed:^{
    // 保存草稿失敗
}];

// 獲取草稿
[self.manager getSelectedModelArrayComplete:^(NSArray<HXPhotoModel *> *modelArray) {
    if (modelArray.count) {
        // 獲取到保存的草稿給manager
        [weakSelf.manager addModelArray:modelArray];
        // 刷新HXPhotoView
        [weakSelf.photoView refreshView];
    }
}];

如何添加網絡/本地圖片、視頻

通過 HXPhotoManager、HXCustomAssetModel 進行添加
/**
根據本地圖片名初始化

@param imageName 本地圖片名
@param selected 是否選中
@return HXCustomAssetModel
*/
+ (instancetype)assetWithLocaImageName:(NSString *)imageName selected:(BOOL)selected;

/**
根據本地UIImage初始化

@param image 本地圖片
@param selected 是否選中
@return HXCustomAssetModel
*/
+ (instancetype)assetWithLocalImage:(UIImage *)image selected:(BOOL)selected;

/**
根據網絡圖片地址初始化

@param imageURL 網絡圖片地址
@param thumbURL 網絡圖片縮略圖地址
@param selected 是否選中
@return HXCustomAssetModel
*/
+ (instancetype)assetWithNetworkImageURL:(NSURL *)imageURL networkThumbURL:(NSURL *)thumbURL selected:(BOOL)selected;

/**
根據本地視頻地址初始化

@param videoURL 本地視頻地址
@param selected 是否選中
@return HXCustomAssetModel
*/
+ (instancetype)assetWithLocalVideoURL:(NSURL *)videoURL selected:(BOOL)selected;

創建HXCustomAssetModel完成后,通過HXPhotoManager對象的這個方法進行添加
/**
添加自定義資源模型
如果圖片/視頻 選中的數量超過最大選擇數時,之后選中的會變為未選中
如果設置的圖片/視頻不能同時選擇時
圖片在視頻前面的話只會將圖片添加到已選數組.
視頻在圖片前面的話只會將視頻添加到已選數組.
如果 type = HXPhotoManagerSelectedTypePhoto 時 會過濾掉視頻
如果 type = HXPhotoManagerSelectedTypeVideo 時 會過濾掉圖片

@param assetArray 模型數組
*/
- (void)addCustomAssetModel:(NSArray<HXCustomAssetModel *> *)assetArray;

// 添加
[self.manager addCustomAssetModel:@[assetModel1, assetModel2, assetModel3, assetModel4, assetModel5, assetModel6]];
// 完成后刷新HXPhotoView
[self.photoView refreshView];  

相關問題

1. pod YYWebImage與YYKit沖突

解決方案:將YYKit拆開分別導入

2. 如何更換語言

HXPhotoConfiguration.h

設置語言類型
HXPhotoLanguageTypeSys = 0, // 跟隨系統語言
HXPhotoLanguageTypeSc,      // 中文簡體
HXPhotoLanguageTypeTc,      // 中文繁體
HXPhotoLanguageTypeJa,      // 日文
HXPhotoLanguageTypeKo,      // 韓文
HXPhotoLanguageTypeEn       // 英文

/**
語言類型
默認 跟隨系統
*/
@property (assign, nonatomic) HXPhotoLanguageType languageType;

3. 選擇完照片后其他界面視圖往下偏移

方法一:
/**
如果選擇完照片返回之后,
原有界面繼承UIScrollView的視圖都往下偏移一個導航欄距離的話,
那么請將這個屬性設置為YES,即可恢復。
*/
@Property (assign, nonatomic) BOOL restoreNavigationBar;

方法二:
在選擇完照片之后加上
[UINavigationBar appearance].translucent = NO;

4. 關于圖片

根據HXPhotoModel的type屬性來區分圖片類型
HXPhotoModelMediaTypePhoto          = 0,    //!< 相冊里的普通照片
HXPhotoModelMediaTypeLivePhoto      = 1,    //!< LivePhoto
HXPhotoModelMediaTypePhotoGif       = 2,    //!< gif圖
HXPhotoModelMediaTypeCameraPhoto    = 5,    //!< 通過相機拍的臨時照片、本地/網絡圖片
當type為HXPhotoModelMediaTypeCameraPhoto時,如果networkPhotoUrl不為空的話,那么這張圖片就是網絡圖片
如果為本地圖片時thumbPhoto/previewPhoto就是本地圖片
不為本地圖片時thumbPhoto/previewPhoto的值都是臨時存的只用于展示
HXPhotoModel已提供方法獲取image或者imageData

5. 關于視頻的URL

1.如果選擇的HXPhotoModel的PHAsset有值,需要先獲取AVAsset,再使用AVAssetExportSession根據AVAsset導出視頻地址
2.如果PHAsset為空的話,則代表此視頻是本地視頻??梢灾苯親XPhotoModel里的VideoURL屬性
HXPhotoModel已提供方法獲取

6. 關于相機拍照

當拍攝的照片/視頻保存到系統相冊
如果系統版本為9.0及以上時,拍照后的照片/視頻保存相冊后會獲取保存后的PHAsset,保存的時候如果有定位信息也會把定位信息保存到相冊
HXPhotoModel里PHAsset有值并且type為 HXPhotoModelMediaTypePhoto / HXPhotoModelMediaTypeVideo
以下版本的和不保存相冊的都只是存在本地的臨時圖片/視頻 
HXPhotoModel里PHAsset為空并且type為 HXPhotoModelMediaTypeCameraPhoto / HXPhotoModelMediaTypeCameraVideo

7. 關于原圖

根據代理或者block回調里的 isOriginal 來判斷是否選擇了原圖 
方法一:
// 獲取原圖
// 本地圖片、網絡圖片調用此方法會直接進入失敗回調
// 本地圖片獲取原圖 model.thumbPhoto / model.previewPhoto
// 網絡圖片獲取原圖 如果 model.thumbPhoto / model.previewPhoto 都為空的話,說明還沒有下載完成或者下載失敗了,重新下載即可。也可以直接用網絡圖片地址 model.networkPhotoUrl 下載 或者調用requestPreviewImageWithSize:progressHandler:success:failed
// 這個方法只針對有photoModel.asset不為空的情況
[photoModel requestImageURLStartRequestICloud:^(PHContentEditingInputRequestID iCloudRequestId, HXPhotoModel *model) { 
    // 如果照片在iCloud上會去下載,此回調代表開始下載iCloud上的照片
    // 如果照片在本地存在此回調則不會走
} progressHandler:^(double progress, HXPhotoModel *model) {
    // iCloud下載進度
} success:^(NSURL *imageURL, HXPhotoModel *model, NSDictionary *info) {
    // 獲取成功
    // imageURL圖片地址
    if ([imageURL.relativePath.pathExtension isEqualToString:@"HEIC"]) {
        // 處理一下 HEIC 格式圖片
        CIImage *ciImage = [CIImage imageWithContentsOfURL:imageURL];
        CIContext *context = [CIContext context];
        NSString *key = (__bridge NSString *)kCGImageDestinationLossyCompressionQuality;
        NSData *jpgData = [context JPEGRepresentationOfImage:ciImage colorSpace:ciImage.colorSpace options:@{key : @1}];
        UIImage *image = [UIImage imageWithData:jpgData];
    }else {
        UIImage *image = [UIImage imageWithContentsOfFile:path]; 
    }
} failed:^(NSDictionary *info, HXPhotoModel *model) {
    // 獲取失敗
}];
// 根據 size 獲取高清圖或者縮略圖 , size只針對 PHAsset 有值的情況下有效
// 如果 size (width <= 0, height <= 0) / PHImageManagerMaximumSize 則會獲取原圖
// 本地圖片直接返回本地圖片的image
// 網絡圖片直接返回網絡圖片下載完成后的image
[photoModel requestPreviewImageWithSize:size startRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel *model) {
    // 如果照片在iCloud上會去下載,此回調代表開始下載iCloud上的照片
    // 如果照片在本地存在此回調則不會走
} progressHandler:^(double progress, HXPhotoModel *model) {
    // iCloud下載進度
    // 如果為網絡圖片,則是網絡圖片的下載進度
} success:^(UIImage *image, HXPhotoModel *model, NSDictionary *info) {
    // 獲取成功
} failed:^(NSDictionary *info, HXPhotoModel *model) {
    // 獲取失敗
}];
方法二:
// 獲取 imageData 根據data來處理
// 如果為網絡圖片的話會先下載
[photoModel requestImageDataStartRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel *model) {
    // 開始下載iCloud上照片的imageData
} progressHandler:^(double progress, HXPhotoModel *model) {
    // iCloud下載進度
} success:^(NSData *imageData, UIImageOrientation orientation, HXPhotoModel *model, NSDictionary *info) {
    // 獲取成功
    if ([HXPhotoTools assetIsHEIF:model.asset]) {
        // 處理一下 HEIC 格式圖片
        CIImage *ciImage = [CIImage imageWithData:imageData];
        CIContext *context = [CIContext context];
        NSData *jpgData = [context JPEGRepresentationOfImage:ciImage colorSpace:ciImage.colorSpace options:@{}];
        // jpgData 轉換后的imageData
    } 
} failed:^(NSDictionary *info, HXPhotoModel *model) {
    // 獲取失敗
}];
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,497評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,305評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,962評論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,727評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,193評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,411評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,945評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,777評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,978評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,216評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,657評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,960評論 2 373

推薦閱讀更多精彩內容