当我们需要调用相机时,通常有两个方案,一是使用系统定制UI的UIImagePickerController,二是使用可以自定义UI的AVCaptureSession。
如果我们不要求自定义UI则可以直接使用UIImagePickerController,可以节省时间。
@interface UIImagePickerController : UINavigationController
我们来看一下整个流程:
1.在项目的info.plist中添加相关使用描述:
NSCameraUsageDescription
这里添加相机权限描述
NSMicrophoneUsageDescription
这里添加麦克风权限描述
NSPhotoLibraryAddUsageDescription
这里添加保存到相册权限描述
NSPhotoLibraryUsageDescription
这里添加相册权限描述
2.初始化UIImagePickerController
@property (strong,nonatomic) UIImagePickerController* pickController;
//在跳转到相机的方法中
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
self.pickController = [[UIImagePickerController alloc]init];
self.pickController.sourceType = UIImagePickerControllerSourceTypeCamera;
self.pickController.mediaTypes = @[@"public.image"];
self.pickController.delegate = self; //代理设置
self.pickController.allowsEditing = NO; //是否提供编辑交互界面 比如说拍完照之后的编辑页面(缩放,剪裁等)
//使用内置编辑控件时,图像选择器控制器会强制执行某些选项。对于照片,强制执行方形裁剪以及最大像素尺寸。对于视频,选择器强制执行最大电影长度和分辨率。如果要让用户指定自定义裁剪,则必须提供自己的编辑UI。
self.pickController.showsCameraControls = NO;//是否显示相机控制按钮
self.pickController.cameraOverlayView = self.cameraOverLayView; //自定义相机控制页面
//如果不需要自定义控制页面可以省略上面两行
//设置闪光灯模式
self.pickController.cameraFlashMode = UIImagePickerControllerCameraFlashModeAuto;
/*
typedef NS_ENUM(NSInteger, UIImagePickerControllerCameraFlashMode) {
UIImagePickerControllerCameraFlashModeOff = -1,
UIImagePickerControllerCameraFlashModeAuto = 0,
UIImagePickerControllerCameraFlashModeOn = 1
}
*/
}else{
return;
}
其中:
[UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
//如果设备可用返回YES 否则返回NO
首先使用该方法来测试设备相机的可用性。
Note: Always call the
[isSourceTypeAvailable:]
class method of the[UIImagePickerController]
class and respect its return value. Never assume that a device has a camera. Even if the device has a camera, this method returnsNO
if the camera is unavailable.
永远要调用这个方法来测试设备相机。永远不要主观认为每个设备都有相机。一个设备即使有摄像头,在那个摄像头不可用时,该方法也会返回NO。
self.pickController.sourceType
// default value is UIImagePickerControllerSourceTypePhotoLibrary.
// 选择要访问的源类型
//typedef NS_ENUM(NSInteger, UIImagePickerControllerSourceType) {
// UIImagePickerControllerSourceTypePhotoLibrary, //图库
// UIImagePickerControllerSourceTypeCamera, //相机
// UIImagePickerControllerSourceTypeSavedPhotosAlbum //相机胶卷
//}
self.pickController.mediaTypes
//指定媒体类型是什么 照片还是视频
//默认为 照片
//通过下一行方法可以返回支持的类型
[UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
//查到很多资料都是"kUTTypeMovie","kUTTypeImage"这两个参数名称但是我测试后发现已经变成下面这两种名称
//"public.image" 照片
//"public.movie" 视频
//如果全部支持可以这么设置
self.pickController.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
//单个支持
self.pickController.mediaTypes = @[@"public.image"];
最后设置完代理后,别忘了遵守协议:
3.实现协议方法:
// 控制器不会自己dismiss 需要我们手动在相应的地方实现
// 这两个代理方法只会收到其中一个,取决于用户的点击情况
//结束采集之后 之后怎么处理都在这里写 通过Infokey取出相应的信息 Infokey可在进入文件中查看
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
//查看是视频还是照片 public.image 或 public.movie
NSString * mediaType = [info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:@"public.image"]) {//照片
UIImage* editedImage =(UIImage *)[info objectForKey:
UIImagePickerControllerEditedImage]; //取出编辑过的照片
UIImage* originalImage =(UIImage *)[info objectForKey:
UIImagePickerControllerOriginalImage];//取出原生照片
UIImage* imageToSave = nil;
if(editedImage){
imageToSave = editedImage;
} else {
imageToSave = originalImage;
}
//将新图像(原始图像或已编辑)保存到相机胶卷
UIImageWriteToSavedPhotosAlbum(imageToSave,nil,nil,nil);
}
}
if ([mediaType isEqualToString:@"public.movie"]) {//视频
}
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker;
//用户点击了取消
4.跳转到相机前确认权限:
AVAuthorizationStatus cameraStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
//相机权限
// typedef NS_ENUM(NSInteger, AVAuthorizationStatus) {
// AVAuthorizationStatusNotDetermined = 0,
// AVAuthorizationStatusRestricted = 1,
// AVAuthorizationStatusDenied = 2,
// AVAuthorizationStatusAuthorized = 3,
// } API_AVAILABLE(macos(10.14), ios(7.0))
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
/* 相册权限检测 需要导入 #import 框架
typedef NS_ENUM(NSInteger, PHAuthorizationStatus) {
PHAuthorizationStatusNotDetermined = 0, // 用户还没有选择
PHAuthorizationStatusRestricted, // 客户端未被授权访问。用户不能改变状态,可能是由于家长控制
PHAuthorizationStatusDenied, // 用户明确拒绝
PHAuthorizationStatusAuthorized // 用户同意访问
} PHOTOS_AVAILABLE_IOS_TVOS_OSX(8_0, 10_0, 10_13);
*/
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
//相册访问权限
if (status == PHAuthorizationStatusAuthorized) {
NSLog(@"Authorized");
}else{
NSLog(@"Denied or Restricted");
}
}];
ps:这里有一个坑:
测试手机版本(12.1.4) 7P
点击拒绝访问用户相册之后还是会进入相册,且下次也可以进入,貌似不需要用户授权即可访问用户相册。
把Info.plist中的相册使用权限描述删除之后 且 把相册权限检测方法删掉 可以直接访问用户相册不报错。
如果添加了相册检测方法则需要在Info.plist文件中添加相册使用描述,否则报错。
在需要跳转的地方执行:
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion:(void (^ __nullable)(void))completion;
即可进入到系统定制UI的相机页面,这时我们可能会发现为什么界面都是英文的呢?
这样做:
在Localizations中添加中文源,然后在target中改成China即可。
5.说下CameraOverLayView
UIImageViewController的这个属性,可以给其赋值实现自定义相机控制view。就是说我们自己写好一个view上面有各种控制按钮,将其替代系统的相机控制按钮(快门键,切换前后置,闪光灯控制等)。
@property(nullable, nonatomic,strong) __kindof UIView *cameraOverlayView NS_AVAILABLE_IOS(3_1); // set a view to overlay the preview view.
需要搭配:
@property(nonatomic) BOOL showsCameraControls NS_AVAILABLE_IOS(3_1); // 是否显示系统标准控制UI。默认YES
6.可能会遇到的问题:
如果在设置为如下sourceType时报错
self.pickController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Source type must be UIImagePickerControllerSourceTypeCamera'
注意:
当我们使用相册的时候 就无法给这个
self.pickController.showsCameraControls
属性赋值
解决办法: 不写这一行,采用默认值;