// // KSMediaManager.m // StudentDaya // // Created by Kyle on 2020/1/3. // Copyright © 2020 DayaMusic. All rights reserved. // #import "KSMediaManager.h" #import #import #import "TZVideoPlayerController.h" #import "TZImagePickerController.h" #import #import "KSPremissionAlert.h" #define COLUMNNUMBER (3) @interface KSMediaManager () { BOOL _isSelectOriginalPhoto; } @property (nonatomic, strong) UIImagePickerController *imagePickerVc; @property (nonatomic, copy) MediaCallback callback; @property (nonatomic, strong) NSString *presentName; @end @implementation KSMediaManager - (instancetype)init { if (self = [super init]) { [self configDefaultConfig]; } return self; } - (void)configDefaultConfig { self.mediaType = MEDIATYPE_PHOTO; self.maxPhotoNumber = 9; self.videoQuality = UIImagePickerControllerQualityType640x480; } - (void)setMediaType:(MEDIATYPE)mediaType { _mediaType = mediaType; if (mediaType == MEDIATYPE_PHOTO) { self.maxPhotoNumber = 9; } else if (mediaType == MEDIATYPE_VIDEO) { self.maxPhotoNumber = 1; } else if (mediaType == MEDIATYPE_ALL) { self.maxPhotoNumber = 2; } } - (void)setVideoQuality:(UIImagePickerControllerQualityType)videoQuality { _videoQuality = videoQuality; if ([[NSString deviceVersion] containsString:@"iPhone 12"]) { if (videoQuality == UIImagePickerControllerQualityType640x480) { _presentName = AVAssetExportPreset640x480; } else if (videoQuality == UIImagePickerControllerQualityTypeIFrame1280x720) { _presentName = AVAssetExportPreset1280x720; } else if (videoQuality == UIImagePickerControllerQualityTypeIFrame960x540) { _presentName = AVAssetExportPreset960x540; } else { _presentName = AVAssetExportPreset640x480; } } else { _presentName = AVAssetExportPresetMediumQuality; } } - (void)showAlertCallbackWithBlock:(MediaCallback)callback { UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:IS_IPAD ? UIAlertControllerStyleAlert : UIAlertControllerStyleActionSheet]; [alertVC addAction:[UIAlertAction actionWithTitle:@"相机拍摄" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { // 调用相机 [self takePhoto]; }]]; [alertVC addAction:[UIAlertAction actionWithTitle:@"从手机相册选择" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { // 调用相册 [self pushImagePickerController]; }]]; [alertVC addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]]; alertVC.modalPresentationStyle = UIModalPresentationFullScreen; [self.baseCtrl presentViewController:alertVC animated:true completion:nil]; if (callback) { self.callback = callback; } } - (void)noAlertCallback:(MediaCallback)callback { if (callback) { self.callback = callback; } } #pragma mark - TZImagePickerController - (void)extracted:(TZImagePickerController *)imagePickerVc { [imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray *photos, NSArray *assets, BOOL isSelectOriginalPhoto) { // 赋值 if (self.callback) { dispatch_main_sync_safe(^{ self.imageArray = [NSMutableArray arrayWithArray:photos]; self.imageAsset = [NSMutableArray arrayWithArray:assets]; self.callback(nil, self.imageArray, self.imageAsset); }) } }]; } - (void)pushImagePickerController { TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:self.maxPhotoNumber columnNumber:COLUMNNUMBER delegate:self]; #pragma mark - 四类个性化设置,这些参数都可以不传,此时会走默认设置 // 1.设置目前已经选中的图片数组 imagePickerVc.selectedAssets = self.imageAsset; // 目前已经选中的图片数组 imagePickerVc.isSelectOriginalPhoto = NO; // 2. Set the appearance // 2. 在这里设置imagePickerVc的外观 [imagePickerVc.navigationBar setBarTintColor:THEMECOLOR]; // 3. 设置是否可以选择视频/图片/原图 if (self.mediaType == MEDIATYPE_ALL) { imagePickerVc.allowTakePicture = YES; imagePickerVc.allowTakeVideo = YES; imagePickerVc.allowPickingVideo = YES; imagePickerVc.allowPickingImage = YES; } else if (self.mediaType == MEDIATYPE_PHOTO) { imagePickerVc.allowTakePicture = YES; imagePickerVc.allowTakeVideo = NO; imagePickerVc.allowPickingVideo = NO; imagePickerVc.allowPickingImage = YES; } else if (self.mediaType == MEDIATYPE_VIDEO) { imagePickerVc.allowTakePicture = NO; imagePickerVc.allowTakeVideo = YES; imagePickerVc.allowPickingVideo = YES; imagePickerVc.allowPickingImage = NO; } imagePickerVc.videoMaximumDuration = self.videoMaxDuration ? self.videoMaxDuration : 480; // 设置视频拍摄质量 [imagePickerVc setUiImagePickerControllerSettingBlock:^(UIImagePickerController *imagePickerController) { imagePickerController.videoQuality = UIImagePickerControllerQualityType640x480; }]; imagePickerVc.allowCrop = self.needCropImage; // 单张才需要裁剪 imagePickerVc.needCircleCrop = NO; imagePickerVc.showSelectBtn = YES; NSInteger left = 30; NSInteger widthHeight = kScreenWidth - 2 * left; NSInteger top = (kScreenHeight - widthHeight) / 2; imagePickerVc.cropRect = CGRectMake(left, top, widthHeight, widthHeight); // 4. 照片排列按修改时间升序 imagePickerVc.sortAscendingByModificationDate = NO; // You can get the photos by block, the same as by delegate. // 你可以通过block或者代理,来得到用户选择的照片. [self extracted:imagePickerVc]; [imagePickerVc setDidFinishPickingVideoHandle:^(UIImage *coverImage, PHAsset *asset) { NSInteger maxDuration = self.videoMaxDuration == 0 ? 480 : self.videoMaxDuration; if (asset.duration >= maxDuration + 1) { NSString *tipsMessage = @""; // 如果不是整分钟,提示秒 如果是整数分钟,提示分钟 if (maxDuration % 60 == 0) { NSInteger minuteNum = maxDuration / 60; tipsMessage = [NSString stringWithFormat:@"视频长度不能超过%zd分钟", minuteNum]; } else { tipsMessage = [NSString stringWithFormat:@"视频长度不能超过%zd秒",self.videoMaxDuration]; } dispatch_main_async_safe(^{ [LOADING_MANAGER MBShowAUTOHidingInWindow:tipsMessage]; }); return; } // PHAssetResource *resource = [[PHAssetResource assetResourcesForAsset:asset] firstObject]; // long long size = [[resource valueForKey:@"fileSize"] longLongValue]; // // NSLog(@"原视频大小:%@",[NSString stringWithFormat:@"%.2fM",(CGFloat)size/(1024*1024)]); dispatch_main_sync_safe(^{ [LOADING_MANAGER MBShowInWindow:@"视频导出中..."]; }); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [[TZImageManager manager] getVideoOutputPathWithAsset:asset presetName:self.presentName success:^(NSString *outputPath) { dispatch_main_sync_safe(^{ [LOADING_MANAGER removeHUDNoDelay]; }); NSLog(@"视频导出到本地完成,沙盒路径为:%@",outputPath); NSData *outputData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:outputPath]]; //压缩后的视频 NSLog(@"导出后的视频:%@",[NSString stringWithFormat:@"%.2fM",(CGFloat)outputData.length/(1024*1024)]); // Export completed, send video here, send by outputPath or NSData // 导出完成,在这里写上传代码,通过路径或者通过NSData上传 if (self.callback) { dispatch_main_sync_safe(^{ self.callback(outputPath, self.imageArray, self.imageAsset); }); } } failure:^(NSString *errorMessage, NSError *error) { dispatch_main_sync_safe(^{ [LOADING_MANAGER removeHUDNoDelay]; [LOADING_MANAGER MBShowAUTOHidingInWindow:@"视频导出失败"]; }); NSLog(@"视频导出失败:%@,error:%@",errorMessage, error); }]; }); }]; imagePickerVc.modalPresentationStyle = UIModalPresentationFullScreen; [self.baseCtrl presentViewController:imagePickerVc animated:YES completion:nil]; } #pragma mark - UIImagePickerController - (void)takePhoto { AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; if ((authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied) && kiOS7Later) { [KSPremissionAlert shareInstanceDisplayImage:CHECKDEVICETYPE_CAMREA message:@"请开启相机访问权限" showInView:self.baseCtrl.view cancel:^{ } confirm:^{ [self openSettingView]; }]; } else if (authStatus == AVAuthorizationStatusNotDetermined) { // 防止用户首次拍照拒绝授权时相机页黑屏 [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { if (granted) { dispatch_async(dispatch_get_main_queue(), ^{ [self takePhoto]; }); } }]; } else if ([PHPhotoLibrary authorizationStatus] == 2) { // 已被拒绝,没有相册权限,将无法保存拍的照片 [KSPremissionAlert shareInstanceDisplayImage:CHECKDEVICETYPE_CAMREA message:@"请开启相册访问权限" showInView:self.baseCtrl.view cancel:^{ } confirm:^{ [self openSettingView]; }]; } else if ([PHPhotoLibrary authorizationStatus] == 0) { // 未请求过相册权限 [[TZImageManager manager] requestAuthorizationWithCompletion:^{ [self takePhoto]; }]; } else { // 调用相机 UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera; if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]) { self.imagePickerVc.sourceType = sourceType; NSMutableArray *mediaTypes = [NSMutableArray array]; if (self.mediaType == MEDIATYPE_ALL) { [mediaTypes addObject:(NSString *)kUTTypeMovie]; [mediaTypes addObject:(NSString *)kUTTypeImage]; } else if (self.mediaType == MEDIATYPE_VIDEO) { [mediaTypes addObject:(NSString *)kUTTypeMovie]; } else { [mediaTypes addObject:(NSString *)kUTTypeImage]; } self.imagePickerVc.mediaTypes = mediaTypes; if(kiOS8Later) { _imagePickerVc.modalPresentationStyle = UIModalPresentationOverCurrentContext; } _imagePickerVc.modalPresentationStyle = UIModalPresentationFullScreen; [self.baseCtrl presentViewController:_imagePickerVc animated:YES completion:nil]; } else { NSLog(@"模拟器中无法打开照相机,请在真机中使用"); } } } // 外部拍照进入的方法 - (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { [picker dismissViewControllerAnimated:YES completion:nil]; NSString *type = [info objectForKey:UIImagePickerControllerMediaType]; TZImagePickerController *tzImagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:self.maxPhotoNumber delegate:self]; [tzImagePickerVc showProgressHUD]; if ([type isEqualToString:@"public.image"]) { tzImagePickerVc.allowCrop = self.needCropImage; tzImagePickerVc.needCircleCrop = NO; tzImagePickerVc.showSelectBtn = YES; tzImagePickerVc.sortAscendingByModificationDate = NO; UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage]; // save photo and get asset / 保存图片,获取到asset [[TZImageManager manager] savePhotoWithImage:image completion:^(PHAsset *asset,NSError *error){ [tzImagePickerVc hideProgressHUD]; if (error) { // 如果保存失败,基本是没有相册权限导致的... [tzImagePickerVc hideProgressHUD]; [KSPremissionAlert shareInstanceDisplayImage:CHECKDEVICETYPE_CAMREA message:@"请开启相册访问权限" showInView:self.baseCtrl.view cancel:^{ } confirm:^{ [self openSettingView]; }]; } else { TZAssetModel *assetModel = [[TZImageManager manager] createModelWithAsset:asset]; if (self.needCropImage) { TZImagePickerController *imagePicker = [[TZImagePickerController alloc] initCropTypeWithAsset:assetModel.asset photo:image completion:^(UIImage *cropImage, id asset) { // 回调 if (self.callback) { self.imageArray = [NSMutableArray arrayWithObject:cropImage]; self.imageAsset = [NSMutableArray arrayWithObject:asset]; dispatch_async(dispatch_get_main_queue(), ^{ self.callback(nil, self.imageArray, self.imageAsset); }); } }]; imagePicker.allowPickingImage = YES; imagePicker.allowCrop = self.needCropImage; imagePicker.needCircleCrop = NO; NSInteger left = 30; NSInteger widthHeight = kScreenWidth - 2 * left; NSInteger top = (kScreenHeight - widthHeight) / 2; imagePicker.cropRect = CGRectMake(left, top, widthHeight, widthHeight); imagePicker.modalPresentationStyle = UIModalPresentationFullScreen; [self.baseCtrl presentViewController:imagePicker animated:YES completion:nil]; } else { if (self.callback) { [self.imageArray addObject:image]; [self.imageAsset addObject:assetModel.asset]; dispatch_async(dispatch_get_main_queue(), ^{ self.callback(nil, self.imageArray, self.imageAsset); }); } } } }]; } else if ([type isEqualToString:@"public.movie"]) { tzImagePickerVc.videoMaximumDuration = self.videoMaxDuration ? self.videoMaxDuration : 480; // 设置视频拍摄质量 [tzImagePickerVc setUiImagePickerControllerSettingBlock:^(UIImagePickerController *imagePickerController) { imagePickerController.videoQuality = UIImagePickerControllerQualityType640x480; }]; NSURL *videoUrl = [info objectForKey:UIImagePickerControllerMediaURL]; if (videoUrl) { [[TZImageManager manager] saveVideoWithUrl:videoUrl completion:^(PHAsset *asset, NSError *error) { // 删除文件 [self removeVideoWithPath:videoUrl.path]; [tzImagePickerVc hideProgressHUD]; if (!error) { dispatch_main_async_safe(^{ [LOADING_MANAGER MBShowInWindow:@"视频处理中..."]; }); [[TZImageManager manager] getVideoOutputPathWithAsset:asset presetName:self.presentName success:^(NSString *outputPath) { // NSData *data = [NSData dataWithContentsOfFile:outputPath]; dispatch_main_async_safe(^{ [LOADING_MANAGER removeHUD]; }); NSLog(@"视频导出到本地完成,沙盒路径为:%@",outputPath); // Export completed, send video here, send by outputPath or NSData // 导出完成,在这里写上传代码,通过路径或者通过NSData上传 NSData *outputData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:outputPath]]; //压缩后的视频 NSLog(@"导出后的视频:%@",[NSString stringWithFormat:@"%.2fM",(CGFloat)outputData.length/(1024*1024)]); // Export completed, send video here, send by outputPath or NSData // 导出完成,在这里写上传代码,通过路径或者通过NSData上传 if (self.callback) { self.callback(outputPath, self.imageArray, self.imageAsset); } } failure:^(NSString *errorMessage, NSError *error) { dispatch_main_async_safe(^{ [LOADING_MANAGER removeHUD]; [LOADING_MANAGER MBShowAUTOHidingInWindow:@"视频导出失败"]; }); NSLog(@"视频导出失败:%@,error:%@",errorMessage, error); }]; } }]; } } } - (void)removeVideoWithPath:(NSString *)videoUrl { NSFileManager *fileMamager = [NSFileManager defaultManager]; if ([fileMamager fileExistsAtPath:videoUrl]) { [fileMamager removeItemAtPath:videoUrl error:nil]; } } - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { if ([picker isKindOfClass:[UIImagePickerController class]]) { [picker dismissViewControllerAnimated:YES completion:nil]; } } #pragma mark - getter - (NSMutableArray *)imageArray { if (!_imageArray) { _imageArray = [NSMutableArray array]; } return _imageArray; } - (NSMutableArray *)imageAsset { if (!_imageAsset) { _imageAsset = [NSMutableArray array]; } return _imageAsset; } - (UIImagePickerController *)imagePickerVc { if (_imagePickerVc == nil) { _imagePickerVc = [[UIImagePickerController alloc] init]; _imagePickerVc.delegate = self; // set appearance / 改变相册选择页的导航栏外观 _imagePickerVc.navigationBar.barTintColor = self.baseCtrl.navigationController.navigationBar.barTintColor; _imagePickerVc.navigationBar.tintColor = self.baseCtrl.navigationController.navigationBar.tintColor; UIBarButtonItem *tzBarItem, *BarItem; if (kiOS9Later) { tzBarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[TZImagePickerController class]]]; BarItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UIImagePickerController class]]]; } else { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" tzBarItem = [UIBarButtonItem appearanceWhenContainedIn:[TZImagePickerController class], nil]; BarItem = [UIBarButtonItem appearanceWhenContainedIn:[UIImagePickerController class], nil]; #pragma clang diagnostic pop } NSDictionary *titleTextAttributes = [tzBarItem titleTextAttributesForState:UIControlStateNormal]; [BarItem setTitleTextAttributes:titleTextAttributes forState:UIControlStateNormal]; } return _imagePickerVc; } - (void)openSettingView { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil]; } @end