大多数app都会涉及到上传照片这个功能,图片来源无非是从相册获取或者相机拍摄。如果不是特别要求,调用系统已经满足需求。但对于特殊需求,就需要自定义相机拍摄界面了。
对于无需定制的相机,使用系统的UIKit库里的UIImagePickerController类,几行代码,几个代理方法就可满足所需。但如果要深度定制,就要系统库AVFoundation内部的相关类。
创建自己的相机管理类CameraManager(继承于NSObject)
.h文件
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
//拍照后的回调,传递拍摄的照片
typedef void(^DidCapturePhotoBlock)(UIImage *stillImage);
@interface PXCameraManager : NSObject
@property (nonatomic, strong) AVCaptureSession *session;//AVCaptureSession对象来执行输入设备和输出设备之间的数据传递
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;//预览图层,来显示照相机拍摄到的画面
@property (nonatomic, strong) AVCaptureDeviceInput *deviceInput;//AVCaptureDeviceInput对象是输入流
@property (nonatomic, strong) AVCaptureStillImageOutput *stillImageOutput;//照片输出流对象
@property (nonatomic, assign) CGRect previewLayerFrame;//拍照区域
/* 为其他类提供的自定义接口 */
//设置拍照区域 (其中targetView为要展示拍照界面的view)
- (void)configureWithtargetViewLayer:(UIView *)targetView previewRect:(CGRect)preivewRect;
//拍照成功回调
- (void)takePicture:(DidCapturePhotoBlock)block;
//添加/移除相机浮层(如果有需求要在相机拍照区域添加浮层的时候使用)
- (void)addCoverImageWithImage:(UIImage *)image;
- (void)removeCoverImageWithImage:(UIImage *)image;
//前后摄像头切换
- (void)switchCameras;
//闪光灯切换
- (void)configCameraFlashlight;
@end
.m文件
@property (nonatomic, strong) UIView *preview;//展现拍照区域的view
@property (nonatomic, strong) UIImageView *coverImageView;//拍照区域浮层
@property (nonatomic, assign) BOOL isCaremaBack;
@property (nonatomic, assign) AVCaptureFlashMode flashMode;
//初始化的时候设置自己想要的默认属性
- (instancetype)init{
self = [super init];
if (self) {
self.isCaremaBack = YES;//默认后置摄像头
self.flashMode = AVCaptureFlashModeAuto;//默认自动闪光灯
}
return self;
}
实现接口的方法
1、准备相关硬件
- (void)configureWithTargetViewLayer:(UIView *)targetView previewRect:(CGRect)preivewRect{
self.preview = targetView;
//开始一些相机相关硬件配置
[self addSession];//创建session
[self addVideoPreviewLayerWithRect:preivewRect];//用session 创建 创建layer
[self addvideoInputBackCamera:self.isCaremaBack];//给session 配置摄像头
[self addVideoFlashlightWithFlashModel:self.flashMode];//配置闪光灯
[self addStillImageOutput];//给session 配置输出
}
2、拍照
#pragma mark -
- (void)takePicture:(DidCapturePhotoBlock)block{
AVCaptureConnection *captureConnection = [self findCaptureConnection];
[captureConnection setVideoScaleAndCropFactor:1.0f];
[_stillImageOutput captureStillImageAsynchronouslyFromConnection:captureConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [UIImage imageWithData:imageData];
//这里可以根据不同需求对拍摄到的照片做一些压缩或者裁剪的处理,这里我就偷懒不弄了。
if (block) {
block(image);
}
}];
}
3、切换摄像头
- (void)switchCameras{
if (!_deviceInput) {
return;
}
[_session beginConfiguration];
[_session removeInput:_deviceInput];
self.isCaremaBack = !self.isCaremaBack;
[self addvideoInputBackCamera:self.isCaremaBack];
[_session commitConfiguration];
}
4、切换闪光灯
- (void)configCameraFlashlight{
switch (self.flashMode) {
case AVCaptureFlashModeAuto:
{
self.flashMode = AVCaptureFlashModeOff;
}
break;
case AVCaptureFlashModeOff:
{
self.flashMode = AVCaptureFlashModeOn;
}
break;
case AVCaptureFlashModeOn:
{
self.flashMode = AVCaptureFlashModeAuto;
}
break;
default:
break;
}
[self addVideoFlashlightWithFlashModel:self.flashMode];
}
添加/移除 浮层
- (void)addCoverImageWithImage:(UIImage *)image{
_coverImageView.image = image;
}
- (void)removeCoverImageWithImage:(UIImage *)image{
_coverImageView.image = nil;
}
下面是配置硬件里的几个方法实现
- (void)addSession{
if (!self.session) {
AVCaptureSession *session = [[AVCaptureSession alloc]init];
self.session = session;
}
}
- (void)addVideoPreviewLayerWithRect:(CGRect)previewRect{
if (!self.previewLayer) {
AVCaptureVideoPreviewLayer *previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:_session];
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
self.previewLayer = previewLayer;
[self.preview.layer addSublayer:self.previewLayer];
}
self.previewLayer.frame = previewRect;
}
- (void)addvideoInputBackCamera:(BOOL)back{
NSArray *devices = [AVCaptureDevice devices];
AVCaptureDevice *frontCamera;
AVCaptureDevice *backCamera;
//获取 前、后 摄像头
for (AVCaptureDevice *device in devices) {
if ([device hasMediaType:AVMediaTypeVideo]) {
if ([device position] == AVCaptureDevicePositionBack) {
backCamera = device;
}else if([device position] == AVCaptureDevicePositionFront){
frontCamera = device;
}
}
}
NSError *error = nil;
if(back){
AVCaptureDeviceInput *backCameraDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:backCamera error:&error];
if (!error) {
if ([_session canAddInput:backCameraDeviceInput]) {
[_session addInput:backCameraDeviceInput];
self.deviceInput = backCameraDeviceInput;
}else{
NSLog(@"出错啦");
}
}
}else{
AVCaptureDeviceInput *frontCameraDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:frontCamera error:&error];
if (!error) {
if ([_session canAddInput:frontCameraDeviceInput]) {
[_session addInput:frontCameraDeviceInput];
self.deviceInput = frontCameraDeviceInput;
}else{
NSLog(@"出错啦");
}
}
}
}
- (void)addVideoFlashlightWithFlashModel:(AVCaptureFlashMode )flashModel{
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
[device lockForConfiguration:nil];
if ([device hasFlash]) {
device.flashMode = self.flashMode;
}
[device unlockForConfiguration];
}
- (void)addStillImageOutput{
if (!self.stillImageOutput) {
AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc]init];
NSDictionary *outPutSettingDict = [[NSDictionary alloc]initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey, nil];
stillImageOutput.outputSettings = outPutSettingDict;
[_session addOutput:stillImageOutput];
self.stillImageOutput = stillImageOutput;
}
}
- (AVCaptureConnection *)findCaptureConnection{
AVCaptureConnection *videoConnection;
for (AVCaptureConnection *connection in _stillImageOutput.connections ) {
for (AVCaptureInputPort *port in connection.inputPorts) {
if ([[port mediaType] isEqual:AVMediaTypeVideo]) {
videoConnection = connection;
return videoConnection;
}
}
}
return nil;
}
到此,一个自定义相机的类就有了,要使用的时候,尽管调用吧。