RQPhotoManager.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. //
  2. // RQPhotoManager.m
  3. // RQCommon
  4. //
  5. // Created by 张嵘 on 2018/11/26.
  6. // Copyright © 2018 张嵘. All rights reserved.
  7. //
  8. #import "RQPhotoManager.h"
  9. #import "RQControllerHelper.h"
  10. #import "TZImageManager.h"
  11. #import "TZLocationManager.h"
  12. #import <AssetsLibrary/AssetsLibrary.h>
  13. /// 位置,use to record the location for photo
  14. static CLLocation * st_location = nil;
  15. /// 是否选中了原图
  16. static BOOL st_isSelectOriginalPhoto = NO;
  17. /// 允许最大选中照片数
  18. static CGFloat const RQMaxImagesCount = 8;
  19. @implementation RQPhotoManager
  20. #pragma mark - PhotoBrowser
  21. + (void)showPhotoBrowser:(UIViewController *)viewController photoURLs:(NSArray<NSURL *> *)photoURLsArray initialPageIndex:(NSUInteger)initialPageIndex delegate:(id<IDMPhotoBrowserDelegate>)delegate{
  22. [self showPhotoBrowser:viewController photoURLs:photoURLsArray initialPageIndex:initialPageIndex animatedFromView:nil scaleImage:nil delegate:delegate];
  23. }
  24. + (void)showPhotoBrowser:(UIViewController *)viewController photoURLs:(NSArray<NSURL *> *)photoURLsArray initialPageIndex:(NSUInteger)initialPageIndex animatedFromView:(UIView *)animatedFromView scaleImage:(UIImage *)scaleImage delegate:(id<IDMPhotoBrowserDelegate>)delegate{
  25. NSArray *photos = [IDMPhoto photosWithURLs:photoURLsArray];
  26. [self showPhotoBrowser:viewController photos:photos initialPageIndex:initialPageIndex animatedFromView:animatedFromView scaleImage:scaleImage delegate:delegate];
  27. }
  28. + (void)showPhotoBrowser:(UIViewController *)viewController photos:(NSArray<IDMPhoto *> *)photosArray initialPageIndex:(NSUInteger)initialPageIndex delegate:(id<IDMPhotoBrowserDelegate>)delegate{
  29. [self showPhotoBrowser:viewController photos:photosArray initialPageIndex:initialPageIndex animatedFromView:nil scaleImage:nil delegate:delegate];
  30. }
  31. + (void)showPhotoBrowser:(UIViewController *)viewController photos:(NSArray<IDMPhoto *> *)photosArray initialPageIndex:(NSUInteger)initialPageIndex animatedFromView:(UIView *)animatedFromView scaleImage:(UIImage *)scaleImage delegate:(id<IDMPhotoBrowserDelegate>)delegate{
  32. IDMPhotoBrowser *browser = [[IDMPhotoBrowser alloc] initWithPhotos:photosArray animatedFromView:animatedFromView];
  33. browser.delegate = delegate;
  34. browser.displayActionButton = NO;
  35. browser.displayArrowButton = NO;
  36. browser.displayCounterLabel = YES;
  37. browser.usePopAnimation = YES;
  38. browser.scaleImage = scaleImage;
  39. browser.autoHideInterface = NO;
  40. browser.forceHideStatusBar = NO;
  41. browser.disableVerticalSwipe = NO;
  42. browser.dismissOnTouch = YES;
  43. browser.displayDoneButton = NO;
  44. if (initialPageIndex>0) [browser setInitialPageIndex:initialPageIndex];
  45. if (viewController==nil) viewController = [RQControllerHelper topViewController];
  46. [viewController presentViewController:browser animated:YES completion:nil];
  47. }
  48. #pragma mark - ImagePicker
  49. + (void)fetchPhotosFromCamera:(UIViewController *)viewController allowCrop:(BOOL)allowCrop completion:(void (^)(UIImage *, id))completion{
  50. viewController = (viewController == nil)?[RQControllerHelper topViewController]:viewController;
  51. /// 检查授权
  52. AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  53. if ((authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied) && [UIDevice currentDevice].systemVersion.floatValue >= 7.0f) {
  54. // 无相机权限 做一个友好的提示 ,这个只是针对 iOS8+
  55. [NSObject rq_showAlertViewWithTitle:@"无法访问你的相机" message:@"请在iPhone的“设置-隐私-相机”选项中,允许轻空访问你相机" confirmTitle:@"设置" cancelTitle:@"取消" confirmAction:^{
  56. [self accessApplicationSetting:NO];
  57. } cancelAction:NULL];
  58. } else if (authStatus == AVAuthorizationStatusNotDetermined) {
  59. // fix issue 466, 防止用户首次拍照拒绝授权时相机页黑屏
  60. if (@available(iOS 7.0, *)) {
  61. [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
  62. if (granted) {
  63. dispatch_sync(dispatch_get_main_queue(), ^{
  64. [self fetchPhotosFromCamera:viewController allowCrop:allowCrop completion:completion];
  65. });
  66. }
  67. }];
  68. }else {
  69. [self fetchPhotosFromCamera:viewController allowCrop:allowCrop completion:completion];
  70. }
  71. // 拍照之前还需要检查相册权限
  72. } else if ([self authorizationStatus] == 2) {
  73. // 已被拒绝,没有相册权限,将无法保存拍的照片 这个只是针对 iOS8+
  74. [NSObject rq_showAlertViewWithTitle:@"无法访问你的相册" message:@"请在iPhone的“设置-隐私-相册”选项中,允许轻空访问你相册" confirmTitle:@"设置" cancelTitle:@"取消" confirmAction:^{
  75. [self accessApplicationSetting:YES];
  76. } cancelAction:NULL];
  77. } else if ([self authorizationStatus] == 0) {
  78. // 未请求过相册权限
  79. [[TZImageManager manager] requestAuthorizationWithCompletion:^{
  80. [self fetchPhotosFromCamera:viewController allowCrop:allowCrop completion:completion];
  81. }];
  82. } else {
  83. /// 访问相机
  84. // 提前定位
  85. [[TZLocationManager manager] startLocationWithSuccessBlock:^(NSArray<CLLocation *> *location) {
  86. if ([location isKindOfClass:[NSArray class]]) {
  87. st_location = location.firstObject;
  88. }else {
  89. st_location = nil;
  90. }
  91. } failureBlock:^(NSError *error) {
  92. st_location = nil;
  93. }];
  94. if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) {
  95. UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera;
  96. UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
  97. // set appearance / 改变相册选择页的导航栏外观
  98. imagePicker.navigationBar.barTintColor = viewController.navigationController.navigationBar.barTintColor;
  99. imagePicker.navigationBar.tintColor = viewController.navigationController.navigationBar.tintColor;
  100. UIBarButtonItem *tzBarItem, *BarItem;
  101. if (@available(iOS 9.0, *)) {
  102. tzBarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[TZImagePickerController class]]];
  103. } else {
  104. #pragma clang diagnostic push
  105. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  106. //这里写出现警告的代码就能实现去除警告
  107. tzBarItem = [UIBarButtonItem appearanceWhenContainedIn:[TZImagePickerController class], nil];
  108. #pragma clang diagnostic pop
  109. }
  110. if (@available(iOS 9.0, *)) {
  111. BarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UIImagePickerController class]]];
  112. } else {
  113. #pragma clang diagnostic push
  114. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  115. //这里写出现警告的代码就能实现去除警告
  116. BarItem = [UIBarButtonItem appearanceWhenContainedIn:[UIImagePickerController class], nil];
  117. #pragma clang diagnostic pop
  118. }
  119. NSDictionary *titleTextAttributes = [tzBarItem titleTextAttributesForState:UIControlStateNormal];
  120. [BarItem setTitleTextAttributes:titleTextAttributes forState:UIControlStateNormal];
  121. imagePicker.sourceType = sourceType;
  122. imagePicker.allowsEditing = allowCrop;
  123. if (@available(iOS 8.0, *)) {
  124. imagePicker.modalPresentationStyle = UIModalPresentationOverCurrentContext;
  125. }
  126. [viewController presentViewController:imagePicker animated:YES completion:nil];
  127. /// 要监听图片完成的代理
  128. [imagePicker setBk_didFinishPickingMediaBlock:^(UIImagePickerController *picker, NSDictionary *info) {
  129. [picker dismissViewControllerAnimated:YES completion:NULL];
  130. /// 获取到的图片
  131. UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
  132. if (!image) image = [info objectForKey:UIImagePickerControllerOriginalImage];
  133. /// 保存图片,获取到asset
  134. [[TZImageManager manager] savePhotoWithImage:image location:st_location completion:^(PHAsset *asset, NSError *error) {
  135. if (!error) !completion?:completion(image,nil); /// 回调图片
  136. }];
  137. }];
  138. /// 监听用户取消的代理
  139. [imagePicker setBk_didCancelBlock:^(UIImagePickerController *picker){
  140. if ([picker isKindOfClass:[UIImagePickerController class]]) [picker dismissViewControllerAnimated:YES completion:NULL];
  141. }];
  142. } else {
  143. NSString *title = nil;
  144. #if TARGET_IPHONE_SIMULATOR
  145. /// 模拟器
  146. title = @"模拟器中无法打开照相机,请在真机中使用";
  147. #else
  148. /// 真机
  149. title = @"无法打开你的相机!";
  150. #endif
  151. [NSObject rq_showAlertViewWithTitle:title message:nil confirmTitle:@"我知道了"];
  152. }
  153. }
  154. }
  155. + (void)fetchPhotosFromAlbum:(UIViewController *)viewController maxImagesCount:(NSInteger)maxImagesCount allowCrop:(BOOL)allowCrop selectedAssets:(NSArray *)selecatedAssets completion:(void (^)(NSArray<UIImage *> *, NSArray *, BOOL, NSArray<NSDictionary *> *))completion cancel:(void (^)(void))cancel{
  156. /// show
  157. if (viewController==nil) viewController = [RQControllerHelper topViewController];
  158. /// imagePicker
  159. TZImagePickerController *imagePicker = [[TZImagePickerController alloc] initWithMaxImagesCount:(maxImagesCount==0)?[self maxImagesCount]:maxImagesCount columnNumber:[self colunmNumber] delegate:nil];
  160. /// 多选才行
  161. if (maxImagesCount>1) {
  162. imagePicker.allowCrop = NO;
  163. if (selecatedAssets) imagePicker.selectedAssets = [NSMutableArray arrayWithArray:selecatedAssets];
  164. }else{
  165. imagePicker.allowCrop = allowCrop;
  166. }
  167. /// 配置configure
  168. [self configureImagePicker:imagePicker];
  169. // You can get the photos by block, the same as by delegate.
  170. // 你可以通过block或者代理,来得到用户选择的照片.
  171. imagePicker.didFinishPickingPhotosWithInfosHandle = ^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto, NSArray<NSDictionary *> *infos) {
  172. /// 记录一下选择原图的状态
  173. [self configureSelectOriginalPhoto:isSelectOriginalPhoto];
  174. /// 回调出去
  175. !completion?:completion(photos,assets,isSelectOriginalPhoto,infos);
  176. };
  177. // viewController.presentingViewController
  178. [viewController presentViewController:imagePicker animated:YES completion:nil];
  179. }
  180. + (void)previewPhotos:(UIViewController *)viewController maxImagesCount:(NSInteger)maxImagesCount selectedAssets:(NSArray *)selecatedAssets selectedPhotos:(NSMutableArray *)selectedPhotos currentIndex:(NSInteger)currentIndex completion:(void (^)(NSArray<UIImage *> *, NSArray *, BOOL, NSArray<NSDictionary *> *))completion cancel:(void (^)(void))cancel
  181. {
  182. /// show
  183. if (viewController==nil) viewController = [RQControllerHelper topViewController];
  184. TZImagePickerController *imagePicker = [[TZImagePickerController alloc] initWithSelectedAssets:selecatedAssets.mutableCopy selectedPhotos:selectedPhotos.mutableCopy index:currentIndex];
  185. imagePicker.maxImagesCount = (maxImagesCount<=0)?[self maxImagesCount]:maxImagesCount;
  186. imagePicker.allowCrop = NO;
  187. /// 配置configure
  188. [self configureImagePicker:imagePicker];
  189. // You can get the photos by block, the same as by delegate.
  190. // 你可以通过block或者代理,来得到用户选择的照片.
  191. [imagePicker setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto){
  192. /// 记录一下选择原图的状态
  193. [self configureSelectOriginalPhoto:isSelectOriginalPhoto];
  194. /// 回调出去
  195. !completion?:completion(photos,assets,isSelectOriginalPhoto,nil);
  196. }];
  197. // viewController.presentingViewController
  198. [viewController presentViewController:imagePicker animated:YES completion:nil];
  199. }
  200. #pragma mark - ImagePicker Helper
  201. + (void)configureImagePicker:(TZImagePickerController *)imagePicker
  202. {
  203. imagePicker.minImagesCount = 0;
  204. imagePicker.allowPreview = YES;
  205. /// 完成按钮可点击
  206. imagePicker.alwaysEnableDoneBtn = YES;
  207. /// 这里需要判断一下
  208. if (imagePicker.allowCrop) {
  209. imagePicker.allowPickingOriginalPhoto = NO;
  210. }else{
  211. imagePicker.allowPickingOriginalPhoto = YES;
  212. }
  213. /// system defalut configure
  214. imagePicker.isSelectOriginalPhoto = [self isSelectOriginalPhoto];
  215. /// 不能选择视频
  216. imagePicker.allowPickingVideo = NO;
  217. //// 是否需要圆形裁剪
  218. imagePicker.needCircleCrop = NO;
  219. /// 圆形裁剪size
  220. imagePicker.circleCropRadius = (RQ_SCREEN_MIN_LENGTH - 60);
  221. /// 配置cropView
  222. imagePicker.cropViewSettingBlock = ^(UIView *cropView) {
  223. cropView.layer.borderColor = [UIColor whiteColor].CGColor;
  224. cropView.layer.borderWidth = .5;
  225. };
  226. NSInteger left = 0;
  227. NSInteger widthHeight = RQ_SCREEN_WIDTH - 2 * left;
  228. NSInteger top = (RQ_SCREEN_HEIGHT - widthHeight) / 2;
  229. /// 裁剪尺寸
  230. imagePicker.cropRect = CGRectMake(left, top, widthHeight, widthHeight);
  231. }
  232. + (void)fetchOriginalPhotoWithAsset:(id)asset completion:(void (^)(UIImage *, NSDictionary *, BOOL))completion{
  233. /// 获取原图
  234. [[TZImageManager manager] getOriginalPhotoWithAsset:asset newCompletion:completion];
  235. }
  236. //// 访问设置 isAblum 相册Or相机
  237. + (void) accessApplicationSetting:(BOOL)isAblum {
  238. if (@available(iOS 10.0, *)) {
  239. if (![[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]) {
  240. [NSObject rq_showAlertViewWithTitle:@"十分抱歉" message:@"无法跳转到隐私设置页面,请手动前往设置页面,谢谢" confirmTitle:@"确定"];
  241. }
  242. } else {
  243. #pragma clang diagnostic push
  244. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  245. //这里写出现警告的代码就能实现去除警告
  246. if (![[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]) {
  247. [NSObject rq_showAlertViewWithTitle:@"十分抱歉" message:@"无法跳转到隐私设置页面,请手动前往设置页面,谢谢" confirmTitle:@"确定"];
  248. }
  249. #pragma clang diagnostic pop
  250. }
  251. }
  252. + (BOOL)isSelectOriginalPhoto{
  253. return st_isSelectOriginalPhoto;
  254. }
  255. + (void)configureSelectOriginalPhoto:(BOOL)selected{
  256. st_isSelectOriginalPhoto = selected;
  257. }
  258. /// 多少列
  259. + (NSInteger)colunmNumber{
  260. return (RQ_IS_IPHONE_5||RQ_IS_IPHONE_4_OR_LESS)?3:4;
  261. }
  262. /// 允许最大选择
  263. + (NSInteger)maxImagesCount{
  264. return RQMaxImagesCount;
  265. }
  266. + (NSInteger)authorizationStatus {
  267. if (@available(iOS 8.0, *)) {
  268. return [PHPhotoLibrary authorizationStatus];
  269. } else {
  270. #pragma clang diagnostic push
  271. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  272. //这里写出现警告的代码就能实现去除警告
  273. return [ALAssetsLibrary authorizationStatus];
  274. #pragma clang diagnostic pop
  275. }
  276. }
  277. @end