RQPhotoManager.m 15 KB

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