iOS自定义相机的实现
本文主要介绍iOS系统上的自定义相机的实现,其实并不难主要包含了,拍摄设备,输入端,输出端,以及抓取图像,保存图像的操作。
简单介绍组要控件
- 上方功能区
- 取消按钮
- 闪光灯按钮
- 相机切换按钮
- 下方功能区
- 拍照按钮
- 重新拍照按钮
- 选择照片按钮
- 图像展示视图
主要代码展示
头文件内容展示
- 定了一个block,用于传输照片信息
#import <UIKit/UIKit.h>
typedef void(^ImageBlock)(NSDictionary *imageDictionary);
@interface CameraViewController : UIViewController
@property (nonatomic, copy) ImageBlock imageblock;
-(void)setImageblock:(void(^)(NSDictionary *imageDictionary))imageblock;
@end
视图控件代码,逻辑代码展示
- 当前视图需要的主要空控件,为了方便操作全部定义为属性
@interface CameraViewController ()<AVCaptureMetadataOutputObjectsDelegate,UIAlertViewDelegate,CAAnimationDelegate>
//捕获设备,通常是前置摄像头,后置摄像头,麦克风(音频输入)
@property(nonatomic)AVCaptureDevice *device;
//AVCaptureDeviceInput 代表输入设备,他使用AVCaptureDevice 来初始化
@property(nonatomic)AVCaptureDeviceInput *input;
//当启动摄像头开始捕获输入
@property(nonatomic)AVCaptureMetadataOutput *output;
//输出
@property (nonatomic)AVCaptureStillImageOutput *ImageOutPut;
//session:由他把输入输出结合在一起,并开始启动捕获设备(摄像头)
@property(nonatomic)AVCaptureSession *session;
//图像预览层,实时显示捕获的图像
@property(nonatomic)AVCaptureVideoPreviewLayer *previewLayer;
//设备
@property (nonatomic, strong)AVCaptureDevice *deveice;
//拍照
@property (nonatomic, strong) UIButton *PhotoButton;
//闪光灯
@property (nonatomic, strong) UIButton *flashButton;
//取消
@property (nonatomic, strong) UIButton *cancleButton;
//切换摄像头
@property (nonatomic, strong) UIButton *changeButton;
//确定选择当前照片
@property (nonatomic, strong) UIButton *selectButton;
//重新拍照
@property (nonatomic, strong) UIButton *reCamButton;
//照片加载视图
@property (nonatomic, strong) UIImageView *imageView;
//对焦区域
@property (nonatomic, strong) UIImageView *focusView;
//上方功能区
@property (nonatomic, strong) UIView *topView;
//下方功能区
@property (nonatomic, strong) UIView *bottomView;
//闪光灯状态
@property (nonatomic, assign) BOOL isflashOn;
//拍到的照片
@property (nonatomic, strong) UIImage *image;
//照片的信息
@property (nonatomic, strong) NSDictionary *imageDict;
//是否可以拍照
@property (nonatomic, assign) BOOL canCa;
//闪光灯模式
@property (nonatomic, assign) AVCaptureFlashMode flahMode;
//前后摄像头
@property (nonatomic, assign) AVCaptureDevicePosition cameraPosition;
//模糊视图
@property (nonatomic, strong) UIVisualEffectView *effectView;
@end
- 使用懒加载,减少主要函数中的代码,看起来更为清爽。
@implementation CameraViewController
#pragma mark - 更改摄像头
-(UIVisualEffectView *)effectView{
if (_effectView == nil) {
UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
_effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
_effectView.frame = CGRectMake(0, 0, ScreenWidth(), ScreenHieght());
_effectView.alpha = 1;
}
return _effectView;
}
#pragma mark - 更改闪光灯状态
-(void)setIsflashOn:(BOOL)isflashOn{
_isflashOn = isflashOn;
[[NSUserDefaults standardUserDefaults] setObject:@(_isflashOn) forKey:@"flashMode"];
if (_isflashOn) {
[self.flashButton setBackgroundImage:[UIImage imageNamed:@"flash_on"] forState:UIControlStateNormal];
}else{
[self.flashButton setBackgroundImage:[UIImage imageNamed:@"flash_off"] forState:UIControlStateNormal];
}
}
#pragma mark - 上方功能区
-(UIView *)topView{
if (!_topView ) {
_topView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth(), 50)];
_topView.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.2];
[_topView addSubview:self.cancleButton];
[_topView addSubview:self.flashButton];
[_topView addSubview:self.changeButton];
}
return _topView;
}
#pragma mark - 取消
-(UIButton *)cancleButton{
if (_cancleButton == nil) {
_cancleButton = [UIButton buttonWithType:UIButtonTypeCustom];
_cancleButton.frame = CGRectMake(20, 10, 60, 30);
[_cancleButton setTitle:@"取消" forState:UIControlStateNormal];
[_cancleButton addTarget:self action:@selector(cancle) forControlEvents:UIControlEventTouchUpInside];
}
return _cancleButton ;
}
#pragma mark - 闪光灯
-(UIButton *)flashButton{
if (_flashButton == nil) {
_flashButton = [UIButton buttonWithType:UIButtonTypeCustom];
_flashButton.frame = CGRectMake((ScreenWidth()-30)/2.0, 10, 30, 30);
[_flashButton addTarget:self action:@selector(FlashOn) forControlEvents:UIControlEventTouchUpInside];
}
return _flashButton;
}
#pragma mark - 切换摄像头
-(UIButton *)changeButton{
if (_changeButton == nil) {
_changeButton = [UIButton buttonWithType:UIButtonTypeCustom];
_changeButton.frame = CGRectMake(ScreenWidth()-40, 10, 30, 30);
[_changeButton setBackgroundImage:[UIImage imageNamed:@"cam"] forState:UIControlStateNormal];
[_changeButton addTarget:self action:@selector(changeCamera) forControlEvents:UIControlEventTouchUpInside];
}
return _changeButton;
}
#pragma mark - 下方功能区
-(UIView *)bottomView{
if (!_bottomView) {
_bottomView = [[UIView alloc] initWithFrame:CGRectMake(0, ScreenHieght()-80, ScreenWidth(), 80)];
_bottomView.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.4];
[_bottomView addSubview:self.reCamButton];
[_bottomView addSubview:self.PhotoButton];
[_bottomView addSubview:self.selectButton];
}
return _bottomView;
}
-(UIButton *)reCamButton{
if (_reCamButton == nil) {
_reCamButton = [UIButton buttonWithType:UIButtonTypeCustom];
_reCamButton.frame = CGRectMake(40, 25, 80, 30);
[_reCamButton addTarget:self action:@selector(reCam) forControlEvents:UIControlEventTouchUpInside];
[_reCamButton setTitle:@"重新拍照" forState:UIControlStateNormal];
[_reCamButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_reCamButton.alpha = 0;
}
return _reCamButton;
}
-(UIButton *)PhotoButton{
if (_PhotoButton == nil) {
_PhotoButton = [UIButton buttonWithType:UIButtonTypeCustom];
_PhotoButton.frame = CGRectMake(ScreenWidth()/2.0-30, 10, 60, 60);
[_PhotoButton setImage:[UIImage imageNamed:@"photograph"] forState: UIControlStateNormal];
[_PhotoButton setImage:[UIImage imageNamed:@"photograph_Select"] forState:UIControlStateNormal];
[_PhotoButton addTarget:self action:@selector(shutterCamera) forControlEvents:UIControlEventTouchUpInside];
}
return _PhotoButton;
}
-(UIButton *)selectButton{
if (_selectButton == nil) {
_selectButton = [UIButton buttonWithType:UIButtonTypeCustom];
_selectButton.frame = CGRectMake(ScreenWidth()-120, 25, 80, 30);
[_selectButton addTarget:self action:@selector(selectImage) forControlEvents:UIControlEventTouchUpInside];
[_selectButton setTitle:@"选择照片" forState:UIControlStateNormal];
[_selectButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
_selectButton.alpha = 0;
}
return _selectButton;
}
#pragma mark - 加载照片的视图
-(UIImageView *)imageView{
if (_imageView == nil) {
_imageView = [[UIImageView alloc]initWithFrame:self.previewLayer.frame];
_imageView.layer.masksToBounds = YES;
_imageView.image = _image;
}
return _imageView;
}
#pragma mark - 对焦区域
-(UIImageView *)focusView{
if (_focusView == nil) {
_focusView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 80, 80)];
_focusView.backgroundColor = [UIColor clearColor];
_focusView.image = [UIImage imageNamed:@"foucs80pt"];
}
return _focusView;
}
#pragma mark - 使用self.session,初始化预览层,self.session负责驱动input进行信息的采集,layer负责把图像渲染显示
-(AVCaptureVideoPreviewLayer *)previewLayer{
if (_previewLayer == nil) {
_previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.session];
_previewLayer.frame = CGRectMake(0, 0, ScreenWidth(), ScreenHieght());
_previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
}
return _previewLayer;
}
-(AVCaptureStillImageOutput *)ImageOutPut{
if (_ImageOutPut == nil) {
_ImageOutPut = [[AVCaptureStillImageOutput alloc] init];
}
return _ImageOutPut;
}
#pragma mark - 初始化输入
-(AVCaptureDeviceInput *)input{
if (_input == nil) {
_input = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:nil];
}
return _input;
}
#pragma mark - 初始化输出
-(AVCaptureMetadataOutput *)output{
if (_output == nil) {
_output = [[AVCaptureMetadataOutput alloc]init];
}
return _output;
}
#pragma mark - 使用AVMediaTypeVideo 指明self.device代表视频,默认使用后置摄像头进行初始化
-(AVCaptureDevice *)device{
if (_device == nil) {
_device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
}
return _device;
}
主要逻辑代码
#pragma mark - 当前视图控制器的初始化
- (instancetype)init
{
self = [super init];
if (self) {
_canCa = [self canUserCamear];
}
return self;
}
-(void)setImageblock:(void (^)(NSDictionary *))imageblock{
_imageblock = imageblock;
}
#pragma mark - 检查相机权限
- (BOOL)canUserCamear{
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (authStatus == AVAuthorizationStatusDenied) {
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"请打开相机权限" message:@"设置-隐私-相机" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:@"取消", nil];
alertView.tag = 100;
[alertView show];
return NO;
}
else{
return YES;
}
return YES;
}
#pragma mark - 视图加载
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor clearColor];
if (_canCa) {
[self customCamera];
[self customUI];
[self FlashOn];
}else{
return;
}
// Do any additional setup after loading the view, typically from a nib.
}
#pragma mark - 自定义视图
- (void)customUI{
[self.view addSubview:self.topView];
[self.view addSubview:self.bottomView];
[self.view addSubview:self.focusView];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(focusGesture:)];
[self.view addGestureRecognizer:tapGesture];
}
#pragma mark - 自定义相机
- (void)customCamera{
//生成会话,用来结合输入输出
self.session = [[AVCaptureSession alloc]init];
if ([self.session canSetSessionPreset:AVCaptureSessionPresetPhoto]) {
self.session.sessionPreset = AVCaptureSessionPresetPhoto;
}
if ([self.session canAddInput:self.input]) {
[self.session addInput:self.input];
}
if ([self.session canAddOutput:self.ImageOutPut]) {
[self.session addOutput:self.ImageOutPut];
}
[self.view.layer addSublayer:self.previewLayer];
//开始启动
[self.session startRunning];
if ([self.device lockForConfiguration:nil]) {
if ([self.device isFlashModeSupported:AVCaptureFlashModeAuto]) {
[self.device setFlashMode:AVCaptureFlashModeAuto];
}
//自动白平衡
if ([self.device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeAutoWhiteBalance]) {
[self.device setWhiteBalanceMode:AVCaptureWhiteBalanceModeAutoWhiteBalance];
}
[self.device unlockForConfiguration];
}
[self focusAtPoint:self.view.center];
}
*闪光灯
#pragma 闪光灯
- (void)FlashOn{
if ([self.device lockForConfiguration:nil]) {
if (self.isflashOn) {
if ([self.device isFlashModeSupported:AVCaptureFlashModeOff]) {
[self.device setFlashMode:AVCaptureFlashModeOff];
self.isflashOn = NO;
//[self.flashButton setTitle:@"关" forState:UIControlStateNormal];
}
}else{
if ([self.device isFlashModeSupported:AVCaptureFlashModeAuto]) {
[self.device setFlashMode:AVCaptureFlashModeAuto];
self.isflashOn = YES;
//[self.flashButton setTitle:@"开" forState:UIControlStateNormal];
}
}
[self.device unlockForConfiguration];
}
}
- 双摄像头切换,切换时使用高斯模糊对试图进行处理
#pragma mark - 相机切换
- (void)changeCamera{
NSUInteger cameraCount = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count];
if (cameraCount > 1) {
self.changeButton.userInteractionEnabled = NO;
[self cutoff];
NSError *error;
CATransition *animation = [CATransition animation];
animation.duration = 1;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.type = @"oglFlip";
animation.delegate = self;
AVCaptureDevice *newCamera = nil;
AVCaptureDeviceInput *newInput = nil;
AVCaptureDevicePosition position = [[self.input device] position];
if (position == AVCaptureDevicePositionFront){
newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
animation.subtype = kCATransitionFromLeft;
self.cameraPosition = AVCaptureDevicePositionBack;
}else {
newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
animation.subtype = kCATransitionFromRight;
self.cameraPosition = AVCaptureDevicePositionFront;
}
newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];
[self.previewLayer addAnimation:animation forKey:nil];
/*
高斯模糊
*/
[self.imageView addSubview:self.effectView];
[self.view insertSubview:self.imageView belowSubview:self.topView];
//
if (newInput != nil) {
[self.session beginConfiguration];
[self.session removeInput:self.input];
if ([self.session canAddInput:newInput]) {
[self.session addInput:newInput];
self.input = newInput;
} else {
[self.session addInput:self.input];
}
[self.session commitConfiguration];
} else if (error) {
NSLog(@"toggle carema failed, error = %@", error);
}
//[self.session startRunning];
}
}
- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position{
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for ( AVCaptureDevice *device in devices )
if ( device.position == position ) return device;
return nil;
}
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
self.changeButton.userInteractionEnabled = YES;
[self.effectView removeFromSuperview];
[self.imageView removeFromSuperview];
if (self.cameraPosition == AVCaptureDevicePositionFront) {
self.flashButton.alpha = 0;
}else if (self.cameraPosition == AVCaptureDevicePositionBack){
self.flashButton.alpha = 1;
}
[self.session startRunning];
}
- 摄像头对焦
#pragma mark - 聚焦
- (void)focusGesture:(UITapGestureRecognizer*)gesture{
CGPoint point = [gesture locationInView:gesture.view];
[self focusAtPoint:point];
}
- (void)focusAtPoint:(CGPoint)point{
CGSize size = self.view.bounds.size;
CGPoint focusPoint = CGPointMake( point.y /size.height ,1-point.x/size.width );
NSError *error;
if ([self.device lockForConfiguration:&error]) {
if ([self.device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
[self.device setFocusPointOfInterest:focusPoint];
[self.device setFocusMode:AVCaptureFocusModeAutoFocus];
}
if ([self.device isExposureModeSupported:AVCaptureExposureModeAutoExpose ]) {
[self.device setExposurePointOfInterest:focusPoint];
[self.device setExposureMode:AVCaptureExposureModeAutoExpose];
}
[self.device unlockForConfiguration];
self.focusView.center = point;
//[self startFocusAnimation];
self.focusView.alpha = 1;
[UIView animateWithDuration:0.2 animations:^{
self.focusView.transform = CGAffineTransformMakeScale(1.25f, 1.25f);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 animations:^{
self.focusView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
} completion:^(BOOL finished) {
[self hiddenFocusAnimation];
}];
}];
}
}
主要功能按钮 拍照按钮
#pragma mark - 拍照
- (void)shutterCamera
{
AVCaptureConnection * videoConnection = [self.ImageOutPut connectionWithMediaType:AVMediaTypeVideo];
if (!videoConnection) {
NSLog(@"take photo failed!");
return;
}
[self.ImageOutPut captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if (imageDataSampleBuffer == NULL) {
return;
}
NSData * imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
self.image = [UIImage imageWithData:imageData];
self.imageDict = @{@"image":self.image,@"info":@{@"PHImageFileUTIKey":@".jpeg"}};
[self.session stopRunning];
//[self.view insertSubview:self.imageView belowSubview:self.PhotoButton];
[self.view insertSubview:self.imageView aboveSubview:self.topView];
NSLog(@"image size = %@",NSStringFromCGSize(self.image.size));
self.topView.alpha = 0;
self.PhotoButton.alpha = 0;
self.reCamButton.alpha = 1;
self.selectButton.alpha = 1;
}];
}
- 保存至相册
#pragma - 保存至相册
- (void)saveImageToPhotoAlbum:(UIImage*)savedImage
{
UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL);
}
// 指定回调方法
- (void)image: (UIImage *) image didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo{
if(error != NULL){
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"保存图片结果提示"
message:@"保存图片失败"
delegate:self
cancelButtonTitle:@"确定"
otherButtonTitles:nil];
[alert show];
}
}
- 回上层视图
#pragma mark - 取消 返回上级
-(void)cancle{
[self.imageView removeFromSuperview];
[self.session stopRunning];
[self.navigationController popViewControllerAnimated:YES];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0 && alertView.tag == 100) {
NSURL * url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
}
}
}
- 重新拍照
#pragma mark - 重新拍照
- (void)reCam{
self.imageView.image = nil;
[self.imageView removeFromSuperview];
[self.session startRunning];
self.topView.alpha = 1;
self.PhotoButton.alpha = 1;
self.reCamButton.alpha = 0;
self.selectButton.alpha = 0;
}
- 选择照片
#pragma mark - 选择照片 返回上级
- (void)selectImage{
[self saveImageToPhotoAlbum:self.image];
self.imageblock(self.image);
[self.navigationController popViewControllerAnimated:YES];
}
-(void)viewDidDisappear:(BOOL)animated{
}
- (void)focusDidFinsh{
self.focusView.hidden = YES;
self.focusView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
//self.focusView.transform=CGAffineTransformMakeScale(0.7f, 0.7f);
}
对焦框动画
- (void)startFocusAnimation{
self.focusView.hidden = NO;
self.focusView.transform = CGAffineTransformMakeScale(1.25f, 1.25f);//将要显示的view按照正常比例显示出来
[UIView beginAnimations:nil context:UIGraphicsGetCurrentContext()];
//[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; //InOut 表示进入和出去时都启动动画
//[UIView setAnimationWillStartSelector:@selector(hiddenFoucsView)];
[UIView setAnimationDidStopSelector:@selector(hiddenFocusAnimation)];
[UIView setAnimationDuration:0.5f];//动画时间
self.focusView.transform = CGAffineTransformIdentity;//先让要显示的view最小直至消失
[UIView commitAnimations]; //启动动画
//相反如果想要从小到大的显示效果,则将比例调换
}
- (void)hiddenFocusAnimation{
[UIView beginAnimations:nil context:UIGraphicsGetCurrentContext()];
//NSDate *DATE = [NSDate date];
//[UIView setAnimationStartDate:[NSDate date]];
[UIView setAnimationDelay:3];
self.focusView.alpha = 0;
[UIView setAnimationDuration:0.5f];//动画时间
[UIView commitAnimations];
}
- (void)hiddenFoucsView{
self.focusView.alpha = !self.focusView.alpha;
}
写在最后
第一次自定义一个相机,代码写的可能不太好理解。
这个项目在码云的git库上有(https://gitee.com/ilongge/PhotoDemo)
如果有任何意见或者建议,或者发现bug(应该是有的),请移步至git库下给予指导,3Q。