一個好用的照片選擇器
github地址:https://github.com/SilenceLove/HXPhotoPicker
照片選擇器 支持 ios8 以上
目錄
特性 - Features
- √ 查看/選擇GIF圖片
- √ 照片、視頻可同時(shí)多選/原圖
- √ 3DTouch預(yù)覽照片
- √ 長按拖動改變順序
- √ 自定義相機(jī)拍照/錄制視頻
- √ 自定義轉(zhuǎn)場動畫
- √ 查看/選擇LivePhoto IOS9.1以上才有用
- √ 支持瀏覽網(wǎng)絡(luò)圖片
- √ 支持自定義裁剪圖片
- √ 觀察系統(tǒng)相冊變化實(shí)時(shí)增刪
- √ 支持傳入本地圖片
- √ 支持在線下載iCloud上的資源
- √ 兩種相冊展現(xiàn)方式(列表、彈窗)
- √ 支持Cell上添加
- √ 同一界面多個不同選擇器
安裝 - Installation
- Cocoapods:
pod 'HXPhotoPicker', '~> 2.2.5'
搜索不到庫或最新版請執(zhí)行pod repo update
- 手動導(dǎo)入:將項(xiàng)目中的“HXPhotoPicker”文件夾拖入項(xiàng)目中
- 網(wǎng)絡(luò)圖片加載使用的是SDWebImage v4.4.1 || YYWebImage v1.0.5
- 如果想要加載網(wǎng)絡(luò)gif圖片請使用YYWebImage
- 使用前導(dǎo)入頭文件 "HXPhotoPicker.h"
要求 - Requirements
- iOS8及以上系統(tǒng)可使用. ARC環(huán)境. - iOS 8 or later. Requires ARC
- 在Xcode8環(huán)境下將項(xiàng)目運(yùn)行在iOS10的設(shè)備/模擬器中堕油,訪問相冊和相機(jī)需要配置三個info.plist文件
- Privacy - Photo Library Usage Description 和 Privacy - Camera Usage Description 以及 Privacy - Microphone Usage Description
- 相機(jī)拍照功能請使用真機(jī)調(diào)試
示例 - Examples
如何獲取照片和視頻
根據(jù)選擇完成后返回的 HXPhotoModel 對象獲取
// 獲取 image
// 如果為網(wǎng)絡(luò)圖片的話會先下載
// size 代表獲取image的質(zhì)量
// PHImageManagerMaximumSize 獲取原圖
[photoModel requestPreviewImageWithSize:PHImageManagerMaximumSize startRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel *model) {
// 如果照片在iCloud上會去下載,此回調(diào)代表開始下載iCloud上的照片
// 如果照片在本地存在此回調(diào)則不會走
} progressHandler:^(double progress, HXPhotoModel *model) {
// iCloud下載進(jìn)度
// 如果為網(wǎng)絡(luò)圖片,則是網(wǎng)絡(luò)圖片的下載進(jìn)度
} success:^(UIImage *image, HXPhotoModel *model, NSDictionary *info) {
// 獲取成功
} failed:^(NSDictionary *info, HXPhotoModel *model) {
// 獲取失敗
}];
// 獲取 imageData
// 如果為網(wǎng)絡(luò)圖片的話會先下載
[photoModel requestImageDataStartRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel *model) {
// 開始下載iCloud上照片的imageData
} progressHandler:^(double progress, HXPhotoModel *model) {
// iCloud下載進(jìn)度
} 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下載進(jìn)度
} 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下載進(jìn)度
} success:^(PHLivePhoto *livePhoto, HXPhotoModel *model, NSDictionary *info) {
// 獲取成功
} failed:^(NSDictionary *info, HXPhotoModel *model) {
// 獲取失敗
}];
// 導(dǎo)出視頻地址
// presetName 視頻導(dǎo)出的質(zhì)量
[photoModel exportVideoWithPresetName:AVAssetExportPresetHighestQuality startRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel *model) {
// 開始下載iCloud上的視頻
} iCloudProgressHandler:^(double progress, HXPhotoModel *model) {
// iCloud下載進(jìn)度
} exportProgressHandler:^(float progress, HXPhotoModel *model) {
// 視頻導(dǎo)出進(jìn)度
} success:^(NSURL *videoURL, HXPhotoModel *model) {
// 導(dǎo)出成功
} failed:^(NSDictionary *info, HXPhotoModel *model) {
// 導(dǎo)出失敗
}];
NSArray+HXExtension
/**
獲取image
如果model是視頻的話,獲取的則是視頻封面
@param original 是否原圖
@param completion imageArray 獲取成功的image數(shù)組, errorArray 獲取失敗的model數(shù)組
*/
- (void)hx_requestImageWithOriginal:(BOOL)original completion:(void (^)(NSArray<UIImage *> * _Nullable imageArray, NSArray<HXPhotoModel *> * _Nullable errorArray))completion;
/**
獲取imageData
@param completion 完成回調(diào)孕似,獲取失敗的不會添加到數(shù)組中
*/
- (void)hx_requestImageDataWithCompletion:(void (^)(NSArray<NSData *> * _Nullable imageDataArray))completion;
/**
獲取AVAsset
@param completion 完成回調(diào)垦细,獲取失敗的不會添加到數(shù)組中
*/
- (void)hx_requestAVAssetWithCompletion:(void (^)(NSArray<AVAsset *> * _Nullable assetArray))completion;
/**
獲取視頻地址
@param presetName AVAssetExportPresetHighestQuality / AVAssetExportPresetMediumQuality
@param completion 完成回調(diào)瞪慧,獲取失敗的不會添加到數(shù)組中
*/
- (void)hx_requestVideoURLWithPresetName:(NSString *)presetName completion:(void (^)(NSArray<NSURL *> * _Nullable videoURLArray))completion;
跳轉(zhuǎn)相冊選擇照片
// 懶加載 照片管理類
- (HXPhotoManager *)manager {
if (!_manager) {
_manager = [[HXPhotoManager alloc] initWithType:HXPhotoManagerSelectedTypePhotoAndVideo];
}
return _manager;
}
// 一個方法調(diào)用
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:@"總數(shù)量:%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 代理返回選擇的圖片以及視頻
/**
點(diǎn)擊完成按鈕
@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;
/**
點(diǎn)擊取消
@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;
// 當(dāng)view更新高度時(shí)調(diào)用
- (void)photoView:(HXPhotoView *)photoView updateFrame:(CGRect)frame;
// 刪除網(wǎng)絡(luò)圖片的地址
- (void)photoView:(HXPhotoView *)photoView deleteNetworkPhoto:(NSString *)networkPhotoUrl;
具體請查看HXPhotoView.h
...
如何保存草稿
通過 HXPhotoManager 對象進(jìn)行存儲
/**
保存模型數(shù)組到本地
@param success 成功
@param failed 失敗
*/
- (void)saveSelectModelArraySuccess:(void (^)(void))success failed:(void (^)(void))failed;
/**
刪除本地保存的模型數(shù)組
@return success or failed
*/
- (BOOL)deleteLocalSelectModelArray;
/**
獲取保存在本地的模型數(shù)組
*/
- (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];
}
}];
如何添加網(wǎng)絡(luò)/本地圖片、視頻
通過 HXPhotoManager荸哟、HXCustomAssetModel 進(jìn)行添加
/**
根據(jù)本地圖片名初始化
@param imageName 本地圖片名
@param selected 是否選中
@return HXCustomAssetModel
*/
+ (instancetype)assetWithLocaImageName:(NSString *)imageName selected:(BOOL)selected;
/**
根據(jù)本地UIImage初始化
@param image 本地圖片
@param selected 是否選中
@return HXCustomAssetModel
*/
+ (instancetype)assetWithLocalImage:(UIImage *)image selected:(BOOL)selected;
/**
根據(jù)網(wǎng)絡(luò)圖片地址初始化
@param imageURL 網(wǎng)絡(luò)圖片地址
@param thumbURL 網(wǎng)絡(luò)圖片縮略圖地址
@param selected 是否選中
@return HXCustomAssetModel
*/
+ (instancetype)assetWithNetworkImageURL:(NSURL *)imageURL networkThumbURL:(NSURL *)thumbURL selected:(BOOL)selected;
/**
根據(jù)本地視頻地址初始化
@param videoURL 本地視頻地址
@param selected 是否選中
@return HXCustomAssetModel
*/
+ (instancetype)assetWithLocalVideoURL:(NSURL *)videoURL selected:(BOOL)selected;
創(chuàng)建HXCustomAssetModel完成后假哎,通過HXPhotoManager對象的這個方法進(jìn)行添加
/**
添加自定義資源模型
如果圖片/視頻 選中的數(shù)量超過最大選擇數(shù)時(shí),之后選中的會變?yōu)槲催x中
如果設(shè)置的圖片/視頻不能同時(shí)選擇時(shí)
圖片在視頻前面的話只會將圖片添加到已選數(shù)組.
視頻在圖片前面的話只會將視頻添加到已選數(shù)組.
如果 type = HXPhotoManagerSelectedTypePhoto 時(shí) 會過濾掉視頻
如果 type = HXPhotoManagerSelectedTypeVideo 時(shí) 會過濾掉圖片
@param assetArray 模型數(shù)組
*/
- (void)addCustomAssetModel:(NSArray<HXCustomAssetModel *> *)assetArray;
// 添加
[self.manager addCustomAssetModel:@[assetModel1, assetModel2, assetModel3, assetModel4, assetModel5, assetModel6]];
// 完成后刷新HXPhotoView
[self.photoView refreshView];
相關(guān)問題
1. pod YYWebImage與YYKit沖突
解決方案:將YYKit拆開分別導(dǎo)入
2. 如何更換語言
HXPhotoConfiguration.h
設(shè)置語言類型
HXPhotoLanguageTypeSys = 0, // 跟隨系統(tǒng)語言
HXPhotoLanguageTypeSc, // 中文簡體
HXPhotoLanguageTypeTc, // 中文繁體
HXPhotoLanguageTypeJa, // 日文
HXPhotoLanguageTypeKo, // 韓文
HXPhotoLanguageTypeEn // 英文
/**
語言類型
默認(rèn) 跟隨系統(tǒng)
*/
@property (assign, nonatomic) HXPhotoLanguageType languageType;
3. 選擇完照片后其他界面視圖往下偏移
方法一:
/**
如果選擇完照片返回之后蛔翅,
原有界面繼承UIScrollView的視圖都往下偏移一個導(dǎo)航欄距離的話,
那么請將這個屬性設(shè)置為YES位谋,即可恢復(fù)山析。
*/
@Property (assign, nonatomic) BOOL restoreNavigationBar;
方法二:
在選擇完照片之后加上
[UINavigationBar appearance].translucent = NO;
4. 關(guān)于圖片
根據(jù)HXPhotoModel的type屬性來區(qū)分圖片類型
HXPhotoModelMediaTypePhoto = 0, //!< 相冊里的普通照片
HXPhotoModelMediaTypeLivePhoto = 1, //!< LivePhoto
HXPhotoModelMediaTypePhotoGif = 2, //!< gif圖
HXPhotoModelMediaTypeCameraPhoto = 5, //!< 通過相機(jī)拍的臨時(shí)照片、本地/網(wǎng)絡(luò)圖片
當(dāng)type為HXPhotoModelMediaTypeCameraPhoto時(shí)掏父,如果networkPhotoUrl不為空的話笋轨,那么這張圖片就是網(wǎng)絡(luò)圖片
如果為本地圖片時(shí)thumbPhoto/previewPhoto就是本地圖片
不為本地圖片時(shí)thumbPhoto/previewPhoto的值都是臨時(shí)存的只用于展示
HXPhotoModel已提供方法獲取image或者imageData
5. 關(guān)于視頻的URL
1.如果選擇的HXPhotoModel的PHAsset有值,需要先獲取AVAsset赊淑,再使用AVAssetExportSession根據(jù)AVAsset導(dǎo)出視頻地址
2.如果PHAsset為空的話爵政,則代表此視頻是本地視頻√杖保可以直接HXPhotoModel里的VideoURL屬性
HXPhotoModel已提供方法獲取
6. 關(guān)于相機(jī)拍照
當(dāng)拍攝的照片/視頻保存到系統(tǒng)相冊
如果系統(tǒng)版本為9.0及以上時(shí)钾挟,拍照后的照片/視頻保存相冊后會獲取保存后的PHAsset,保存的時(shí)候如果有定位信息也會把定位信息保存到相冊
HXPhotoModel里PHAsset有值并且type為 HXPhotoModelMediaTypePhoto / HXPhotoModelMediaTypeVideo
以下版本的和不保存相冊的都只是存在本地的臨時(shí)圖片/視頻
HXPhotoModel里PHAsset為空并且type為 HXPhotoModelMediaTypeCameraPhoto / HXPhotoModelMediaTypeCameraVideo
7. 關(guān)于原圖
根據(jù)代理或者block回調(diào)里的 isOriginal 來判斷是否選擇了原圖
方法一:
// 獲取原圖
// 本地圖片饱岸、網(wǎng)絡(luò)圖片調(diào)用此方法會直接進(jìn)入失敗回調(diào)
// 本地圖片獲取原圖 model.thumbPhoto / model.previewPhoto
// 網(wǎng)絡(luò)圖片獲取原圖 如果 model.thumbPhoto / model.previewPhoto 都為空的話掺出,說明還沒有下載完成或者下載失敗了,重新下載即可苫费。也可以直接用網(wǎng)絡(luò)圖片地址 model.networkPhotoUrl 下載 或者調(diào)用requestPreviewImageWithSize:progressHandler:success:failed
// 這個方法只針對有photoModel.asset不為空的情況
[photoModel requestImageURLStartRequestICloud:^(PHContentEditingInputRequestID iCloudRequestId, HXPhotoModel *model) {
// 如果照片在iCloud上會去下載,此回調(diào)代表開始下載iCloud上的照片
// 如果照片在本地存在此回調(diào)則不會走
} progressHandler:^(double progress, HXPhotoModel *model) {
// iCloud下載進(jìn)度
} 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) {
// 獲取失敗
}];
// 根據(jù) size 獲取高清圖或者縮略圖 , size只針對 PHAsset 有值的情況下有效
// 如果 size (width <= 0, height <= 0) / PHImageManagerMaximumSize 則會獲取原圖
// 本地圖片直接返回本地圖片的image
// 網(wǎng)絡(luò)圖片直接返回網(wǎng)絡(luò)圖片下載完成后的image
[photoModel requestPreviewImageWithSize:size startRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel *model) {
// 如果照片在iCloud上會去下載,此回調(diào)代表開始下載iCloud上的照片
// 如果照片在本地存在此回調(diào)則不會走
} progressHandler:^(double progress, HXPhotoModel *model) {
// iCloud下載進(jìn)度
// 如果為網(wǎng)絡(luò)圖片,則是網(wǎng)絡(luò)圖片的下載進(jìn)度
} success:^(UIImage *image, HXPhotoModel *model, NSDictionary *info) {
// 獲取成功
} failed:^(NSDictionary *info, HXPhotoModel *model) {
// 獲取失敗
}];
方法二:
// 獲取 imageData 根據(jù)data來處理
// 如果為網(wǎng)絡(luò)圖片的話會先下載
[photoModel requestImageDataStartRequestICloud:^(PHImageRequestID iCloudRequestId, HXPhotoModel *model) {
// 開始下載iCloud上照片的imageData
} progressHandler:^(double progress, HXPhotoModel *model) {
// iCloud下載進(jìn)度
} 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 轉(zhuǎn)換后的imageData
}
} failed:^(NSDictionary *info, HXPhotoModel *model) {
// 獲取失敗
}];