基于 UIImagePickerController 的拓展封装 - iOS

基于 UIImagePickerController 的拓展,分别支持调用相机、相册的功能,其中相机可以设置默认调用前后摄像头;

简单对此进行了封装和实现,其中还有很多点可以继续挖掘和优化,该版本具体 code 如下:

GitHub 链接地址

声明:

/*
 相机管理
 
 1.cameraType(相机类型)
 通用:universal   相机 Camera   相册 PhotoLib
 2.deviceType(设备类型)
 前置 Front 或者后置 Rear
 */

#import <Foundation/Foundation.h>

@protocol YHCameraManagerDelegate <NSObject>

/** 事件回调*/
- (void)YHCameraCallBack:(UIImage *)image;

@end



@interface YHCameraManager : NSObject

@property (nonatomic, assign) id <YHCameraManagerDelegate> delegate;

/** 单例对象*/
+ (instancetype)shareInstance;

/// 获取相机权限状态
- (BOOL)getCameraPermissionsState;

/// 检测相机权限是否开始并弹出是否去开启
/// @param controller 视图控件
- (void)getCameraPermissionsWithController:(UIViewController *)controller;

/**
 调用相机功能

 @param cameraType 相机类型(相机 & 相册)
 @param deviceType 设备类型(前置摄像头 & 后置摄像头)
 @param controller 控件 VC
 */
- (void)callCameraWithCameraType:(NSString *)cameraType AndDeviceType:(NSString *)deviceType AndController:(UIViewController *)controller;

/**
 调用相机或相册
 
 @param deviceType  摄像头设备类型(默认后置摄像头,前置摄像头需将 deviceType 初始值设置为 @"Front")
 @param controller  当前 VC 控件
 */
- (void)openCameraOrPhotoLibraryWithCameraDeviceType:(NSString *)deviceType AndController:(UIViewController *)controller;

/**
 调用相机拍照(Push 新页面)
 
 @param deviceType 摄像头设备类型(默认后置摄像头,前置摄像头需将 deviceType 初始值设置为 @"Front")
 @param controller 当前 VC 控件
 */
- (void)openCameraWithCameraDeviceType:(NSString *)deviceType AndController:(UIViewController *)controller;

/**
 调用相机拍照(当前视图)
 
 @param deviceType 摄像头设备类型(默认后置摄像头,前置摄像头需将 deviceType 初始值设置为 @"Front")
 @param controller 当前 VC 控件
 */
- (void)openCameraWithCameraDeviceType:(NSString *)deviceType AndCurrentControllerView:(UIViewController *)controller;

/**
 调用相机拍摄

 @param deviceType deviceType 摄像头设备类型(默认后置摄像头,前置摄像头需将 deviceType 初始值设置为 @"Front")
 @param controller controller 当前 VC 控件
 */
- (void)openCameraVideoWithCameraDeviceType:(NSString *)deviceType AndController:(UIViewController *)controller;

/**
 调用相册

 @param controller 当前 VC 控件
 */
- (void)openPhotoLibraryWithController:(UIViewController *)controller;



/**
 图片转 Base64

 @param img     原图片
 @param type    图片类型(PNG 或 JPEG)
 @return        处理结果
 */
+ (NSString *)imageBase64EncodedWithImage:(UIImage *)img AndImageType:(NSString *)type;

/**
 Base64 图片转 UImage

 @param str 原 Base64 图片
 @return    处理结果
 */
+ (UIImage *)imageBase64DecodedWithImageStr:(NSString *)str;

@end

实现:

#import "YHCameraManager.h"
#import <AssetsLibrary/AssetsLibrary.h>// 资源库 iOS 10 被标为弃用,建议使用 Photos framework 的 PHPhotoLibrary
#import <Photos/Photos.h>// iOS 9.0

@interface YHCameraManager () <UIImagePickerControllerDelegate,UINavigationControllerDelegate>

@end

@implementation YHCameraManager

#pragma mark - ****************************** Base
+ (instancetype)shareInstance {
    static YHCameraManager *singleton = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        singleton = [[YHCameraManager alloc] init];
    });
    
    return singleton;
}

#pragma mark - ****************************** Interface methods
/// 获取相机权限状态
- (BOOL)getCameraPermissionsState {
    NSString *mediaType = AVMediaTypeVideo;//读取媒体类型
    AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType];//读取设备授权状态
    /*
     AVAuthorizationStatusNotDetermined 没有对应用程序授权进行操作
     AVAuthorizationStatusRestricted    没有授权访问的照片数据
     AVAuthorizationStatusDenied        用户拒绝对应用程序授权
     AVAuthorizationStatusAuthorized    用户对应用程序授权
     */
    if(authStatus == AVAuthorizationStatusNotDetermined || authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied){
        return NO;
    } else {
        return YES;
    }
}

/// 检测相机权限是否开始并弹出是否去开启
- (void)getCameraPermissionsWithController:(UIViewController *)controller {
    if (![self getCameraPermissionsState]) {
        UIAlertController *alterCon = [UIAlertController alertControllerWithTitle:@"是否开启相机访问权限?"
                                                                          message:nil
                                                                   preferredStyle:UIAlertControllerStyleAlert];
        UIAlertAction *OFF = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:nil];
        UIAlertAction *ON = [UIAlertAction actionWithTitle:@"开启" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) { // 设置权限
            NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
            if ([[UIApplication sharedApplication] canOpenURL:url]) {
                [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:^(BOOL success) {
                    
                }];
            }
        }];
        [alterCon addAction:OFF];
        [alterCon addAction:ON];
        [controller presentViewController:alterCon animated:YES completion:nil];
    }
}

/**
 调用相机功能
 
 @param cameraType 相机类型
 @param deviceType 设备类型
 @param controller 控件 VC
 */
- (void)callCameraWithCameraType:(NSString *)cameraType AndDeviceType:(NSString *)deviceType AndController:(UIViewController *)controller {
    if ([cameraType isEqualToString:@"Camera"]) {// 相机
        [self openCameraWithCameraDeviceType:deviceType
                               AndController:controller];
    } else if ([cameraType isEqualToString:@"PhotoLib"]) {// 相册
        [self openPhotoLibraryWithController:controller];
    } else {// 通用
        [self openCameraOrPhotoLibraryWithCameraDeviceType:deviceType AndController:controller];
    }
}



/**
 调用相机或相册(默认后置摄像头,前置摄像头需将 deviceType 初始值设置为 @"Front")

 @param deviceType  摄像头设备类型
 @param controller  当前 VC 控件
 */
- (void)openCameraOrPhotoLibraryWithCameraDeviceType:(NSString *)deviceType AndController:(UIViewController *)controller {
    UIAlertControllerStyle style = UIAlertControllerStyleActionSheet; // 注:UIAlertControllerStyleActionSheet 仅支持 iPhone,iPad 需使用 UIAlertControllerStyleAlert
    if (kISiPad) {
        style = UIAlertControllerStyleAlert;
    }
    UIAlertController *alertCon = [UIAlertController alertControllerWithTitle:nil
                                                                      message:nil
                                                               preferredStyle:style];
    
    kWeakSelf(self);
    [alertCon addAction:[UIAlertAction actionWithTitle:@"拍照" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        
        
        [weakself openCameraWithCameraDeviceType:deviceType AndController:controller];
        
    }]];
    [alertCon addAction:[UIAlertAction actionWithTitle:@"从相册选择" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        [weakself openPhotoLibraryWithController:controller];
        
    }]];
    
    [alertCon addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        
    }]];
    
    [controller presentViewController:alertCon animated:YES completion:^{
        
    }];
}



/**
 调用相机拍照(默认后置摄像头,前置摄像头需将 deviceType 初始值设置为 @"Front")

 @param deviceType 摄像头设备类型
 @param controller 当前 VC 控件
 */
- (void)openCameraWithCameraDeviceType:(NSString *)deviceType AndController:(UIViewController *)controller {
    // 判断是否可以打开照相机
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
        UIImagePickerController *pickerCon = [[UIImagePickerController alloc] init];
        pickerCon.delegate = self;
        pickerCon.allowsEditing = YES;// 设置拍摄的照片是否允许编辑
        
        // 摄像头
        pickerCon.sourceType = UIImagePickerControllerSourceTypeCamera;
        pickerCon.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;// 设置拍照类型(拍照 & 摄像)
        pickerCon.cameraFlashMode = UIImagePickerControllerCameraFlashModeOff;// 闪光灯,默认:关闭状态
        
        if ([deviceType isEqualToString:@"Front"]) {// 设置使用手机摄像头类型
            pickerCon.cameraDevice = UIImagePickerControllerCameraDeviceFront;// 设置使用手机前置摄像头
        } else {
            pickerCon.cameraDevice = UIImagePickerControllerCameraDeviceRear;// 设置使用手机后置摄像头
        }        
        [controller presentViewController:pickerCon animated:YES completion:^{
            NSLog(@"调用了 --- 摄像头");
        }];
    } else {
        [MBProgressHUD showCommonHudWithAlertString:@"当前未开启相机权限" afterDelay:2.0 toView:controller.view];
    }
}

/**
 调用相机拍照(当前视图)
 
 @param deviceType 摄像头设备类型(默认后置摄像头,前置摄像头需将 deviceType 初始值设置为 @"Front")
 @param controller 当前 VC 控件
 */
- (void)openCameraWithCameraDeviceType:(NSString *)deviceType AndCurrentControllerView:(UIViewController *)controller {
    // 判断是否可以打开相机
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
        UIImagePickerController *pickerCon = [[UIImagePickerController alloc] init];
        pickerCon.sourceType = UIImagePickerControllerSourceTypeCamera;
        pickerCon.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto; // 设置拍照类型(拍照 & 摄像)
        pickerCon.cameraFlashMode = UIImagePickerControllerCameraFlashModeOff; // 闪光灯,默认:关闭状态
        pickerCon.showsCameraControls = NO;
        
        pickerCon.delegate = self;
        pickerCon.allowsEditing = NO; // 设置拍照的照片是否允许编辑
        
        UIView *overLayView = [[UIView alloc] initWithFrame:controller.view.bounds]; // CGRectMake(10, 300, 300, 300)
        
        UIImageView *crosshairs = [[UIImageView alloc]initWithFrame:CGRectMake(20, 100, 400, 400)];
        crosshairs.frame = CGRectMake(50, 100, 100, 100);
        crosshairs.backgroundColor = [UIColor blueColor];
        crosshairs.image = [UIImage imageNamed:@"launchIconImg"];
//        crosshairs.alpha = 0.5f;
        crosshairs.contentMode = UIViewContentModeCenter;
        [overLayView addSubview:crosshairs];
        //在叠加视图上自定义一个拍照按钮
        UIButton *takePhotoBtn=[UIButton buttonWithType:UIButtonTypeCustom]; // 人保寿险易服务 首页 服务大厅 保单变更
        [takePhotoBtn setFrame:CGRectMake(10, 10, 40, 40)];
        [takePhotoBtn setTitle:@"选取图片" forState:UIControlStateNormal];
        [takePhotoBtn addTarget:self action:@selector(takePhoto:) forControlEvents:UIControlEventTouchUpInside];
        [overLayView addSubview:takePhotoBtn];
        
        if ([deviceType isEqualToString:@"Front"]) { // 设置使用手机摄像头类型
            pickerCon.cameraDevice = UIImagePickerControllerCameraDeviceFront; // 前置摄像头
        } else {
            pickerCon.cameraDevice = UIImagePickerControllerCameraDeviceRear; // 后置摄像头
        }
        [controller.view addSubview:pickerCon.view];
    } else {
        [MBProgressHUD showCommonHudWithAlertString:@"当前未开启相机权限" afterDelay:2.0 toView:controller.view];
    }
}

- (void)takePhoto:(id)sender{
    NSLog(@"111111111");
    NSLog(@"%@", sender);
}

/**
 调用相机拍摄
 
 @param deviceType deviceType 摄像头设备类型(默认后置摄像头,前置摄像头需将 deviceType 初始值设置为 @"Front")
 @param controller controller 当前 VC 控件
 */
- (void)openCameraVideoWithCameraDeviceType:(NSString *)deviceType AndController:(UIViewController *)controller {
    // 判断是否可以打开照相机
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
        UIImagePickerController *pickerCon = [[UIImagePickerController alloc] init];
        pickerCon.delegate = self;
        pickerCon.allowsEditing = YES;// 设置拍摄的照片是否允许编辑
        // 摄像头
        pickerCon.sourceType = UIImagePickerControllerSourceTypeCamera;
        pickerCon.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];// 将 mediaType 设置为所有支持的媒体类型
        pickerCon.cameraCaptureMode = UIImagePickerControllerCameraCaptureModeVideo;// 设置拍照类型(拍照 & 摄像)
        pickerCon.videoQuality = UIImagePickerControllerQualityTypeHigh;// 设置拍摄视频的清晰度,高清模式
        if ([deviceType isEqualToString:@"Front"]) {// 设置使用手机摄像头类型
            pickerCon.cameraDevice = UIImagePickerControllerCameraDeviceFront;// 设置使用手机前置摄像头
        } else {
            pickerCon.cameraDevice = UIImagePickerControllerCameraDeviceRear;// 设置使用手机后置摄像头
        }
        
        [controller presentViewController:pickerCon animated:YES completion:^{
            NSLog(@"调用了 --- 摄像头");
        }];
    } else {
        [MBProgressHUD showCommonHudWithAlertString:@"当前未开启相机权限" afterDelay:2.0 toView:controller.view];
    }
}

/**
 调用相册
 
 @param controller 当前 VC 控件
 */
- (void)openPhotoLibraryWithController:(UIViewController *)controller {
    if (@available(iOS 11, *)) {
        UIScrollView.appearance.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAutomatic;
    }
    
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
        UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
        imagePicker.allowsEditing = YES;
        imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
        imagePicker.delegate = self;
        
        [controller presentViewController:imagePicker animated:YES completion:^{
            NSLog(@"调用了 --- 相册");
        }];
    } else {
        [MBProgressHUD showCommonHudWithAlertString:@"无法打开相册" afterDelay:2.0 toView:controller.view];
    }
}



#pragma mark - ****************************** UIImagePickerControllerDelegate
/**
 拍照完成回调

 @param picker  控件
 @param info    数据
 */
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
    NSLog(@"UIImagePickerControllerDelegate --- FinishPickingMedia");
    
    // 获取拍摄数据的类型(照片 or 视频)
//    NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
    
    if (picker.sourceType == UIImagePickerControllerSourceTypeCamera || picker.sourceType == UIImagePickerControllerSourceTypePhotoLibrary) {// 图片 [mediaType isEqualToString:(NSString *)kUTTypeImage]
        UIImage *theImg = nil;
        if ([picker allowsEditing]) {// 判断图片是否允许修改
            // 获取用户编辑之后的图像
            theImg = [info objectForKey:UIImagePickerControllerEditedImage];
        } else {// 获取原图
            theImg = [info objectForKey:UIImagePickerControllerOriginalImage];
        }
        
        // 保存图片至相册中
        UIImageWriteToSavedPhotosAlbum(theImg, self, nil, nil);
        
        // 图片后续处理相关
        if ([self.delegate respondsToSelector:@selector(YHCameraCallBack:)]) {
            [self.delegate YHCameraCallBack:theImg];
        }
    } else {// 视频
        // 获取视频文件 url
        NSURL *urlMedia = [info objectForKey:UIImagePickerControllerMediaType];
        
        if (@available(iOS 9, *)) {
            // 获取照片库的对象
            PHPhotoLibrary *photoLib = [PHPhotoLibrary sharedPhotoLibrary];
            // 资源信息获取的唯一标识
            __block NSString *createdAssetId = nil;
            // 'writeVideoAtPathToSavedPhotosAlbum:completionBlock:' is deprecated: first deprecated in iOS 9.0 - Use creationRequestForAssetFromVideoAtFilePath: on PHAssetChangeRequest from the Photos framework to create a new asset instead
            [photoLib performChanges:^{
                PHAssetChangeRequest *assetRequest = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:urlMedia];// 将资源放入请求等待处理
                /*
                 将处理后的结果存储至相册中的某个位置
                 album: 用户创建
                 smart: 系统创建
                 moment:系统生成的时间分组
                 options:nil 默认获取参数
                 lastObj or firstObj:仅返回一个相册情况下即都一样,若以 album 或 moment 的话,可能会返回多个,last 即取最后一个
                 */
                PHAssetCollection *assetCollection = [[PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum
                                                                                               subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary
                                                                                               options:nil] lastObject];
                PHAssetCollectionChangeRequest *changeCollectionRequest = [PHAssetCollectionChangeRequest changeRequestForAssetCollection:assetCollection];// 建立请求
                PHObjectPlaceholder *assetPlaceholder = [assetRequest placeholderForCreatedAsset];// 创建模型对象,提供通过变更请求
                
                [changeCollectionRequest addAssets:@[assetPlaceholder]];// 执行处理任务
                
            } completionHandler:^(BOOL success, NSError * _Nullable error) {
                if (error) {
                    NSLog(@"%@", error);
                }
            }];
            
        } else {
            // 创建 ALAssetsLibrary 对象并将视频保存到媒体库
            ALAssetsLibrary *assetsLib = [[ALAssetsLibrary alloc] init];
            // 将视频保存至相册
            [assetsLib writeVideoAtPathToSavedPhotosAlbum:urlMedia completionBlock:^(NSURL *assetURL, NSError *error) {
                if (error) {
                    NSLog(@"视频拍摄 --- 写入失败:%@", error);
                } else {
                    NSLog(@"视频拍摄 --- 写入成功");
                }
            }];
        }
        
    }
    
    [picker dismissViewControllerAnimated:YES completion:^{
        
    }];
}

/**
 拍照页面取消选择的时候,调用该方法

 @param picker 当前控件
 */
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
    [picker dismissViewControllerAnimated:YES completion:^{
        
    }];
}



#pragma mark - ****************************** Expand
/**
 获取图片详细信息
 
 图片名称、唯一标示、路径 URL
 
 @param imgUrl 当前图片资源
 */
- (void)getImageDetailInfo:(NSURL *)imgUrl {
    __block NSString *imgFileName;
    ALAssetsLibrary *assetsLib = [[ALAssetsLibrary alloc] init];
    [assetsLib assetForURL:imgUrl resultBlock:^(ALAsset *asset) {
        ALAssetRepresentation *representation = [asset defaultRepresentation];
        imgFileName = [representation filename];
        NSLog(@"Image info:\nImage UTI --- %@\nImage URL --- %@\nFile Path --- %@", [representation UTI], [representation url], imgFileName);
        
    } failureBlock:^(NSError *error) {
        NSLog(@"GetImageDetailInfo --- Error\n%@", error);
    }];
    
}



/**
 图片转 Base64
 
 @param img     原图片
 @param type    图片类型(PNG 或 JPEG)
 @return        处理结果
 */
+ (NSString *)imageBase64EncodedWithImage:(UIImage *)img AndImageType:(NSString *)type {
    NSString *callBack = nil;
    if ([img isKindOfClass:[UIImage class]]) {
        NSData *data = [NSData data];
        if ([type isEqualToString:@"PNG"]) {
            data = UIImagePNGRepresentation(img);
        } else {
            data = UIImageJPEGRepresentation(img, 1.0f);
        }
        
        NSString *encodedImgStr = [data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
        
        NSLog(@"YHCameraManager\nencodedImgStr: %@", encodedImgStr);
        return encodedImgStr;
    } else {
        return callBack;
    }
}

/**
 Base64 图片转 UImage
 
 @param str 原 Base64 图片
 @return    处理结果
 */
+ (UIImage *)imageBase64DecodedWithImageStr:(NSString *)str {
    if (str == nil) {
        return nil;
    } else {
        NSData *decodedImageData = [[NSData alloc] initWithBase64EncodedString:str
                                                                       options:NSDataBase64DecodingIgnoreUnknownCharacters];
        UIImage *decodedImg = [UIImage imageWithData:decodedImageData];
        
        NSLog(@"YHCameraManager\ndecodedImgStr: %@", decodedImg);
        return decodedImg;
    }
}

@end

以上便是此次的分析内容,还望大神多多指教!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值