iOS中 自定义系统相机 作者:韩俊强
需要框架:
#import <AVFoundation/AVFoundation.h>
#import <AssetsLibrary/AssetsLibrary.h>
布局如下:
相关属性:
#import "ViewController.h" #import <AVFoundation/AVFoundation.h> #import <AssetsLibrary/AssetsLibrary.h> typedef void(^PropertyChangeBlock)(AVCaptureDevice *captureDevice); @interface ViewController () @property (strong,nonatomic) AVCaptureSession *captureSession;//负责输入和输出设置之间的数据传递 @property (strong,nonatomic) AVCaptureDeviceInput *captureDeviceInput;//负责从AVCaptureDevice获得输入数据 @property (strong,nonatomic) AVCaptureStillImageOutput *captureStillImageOutput;//照片输出流 @property (strong,nonatomic) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;//相机拍摄预览图层 @property (weak, nonatomic) IBOutlet UIView *viewContainer; @property (weak, nonatomic) IBOutlet UIButton *takeButton;//拍照按钮 @property (weak, nonatomic) IBOutlet UIButton *flashAutoButton;//自动闪光灯按钮 @property (weak, nonatomic) IBOutlet UIButton *flashOnButton;//打开闪光灯按钮 @property (weak, nonatomic) IBOutlet UIButton *flashOffButton;//关闭闪光灯按钮 @property (weak, nonatomic) IBOutlet UIImageView *focusCursor; //聚焦光标 @end @implementation ViewController
#pragma mark - 控制器视图方法 - (void)viewDidLoad { [super viewDidLoad]; } -(void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated]; //初始化会话 _captureSession=[[AVCaptureSession alloc]init]; if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {//设置分辨率 _captureSession.sessionPreset=AVCaptureSessionPreset1280x720; } //获得输入设备 AVCaptureDevice *captureDevice=[self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];//取得后置摄像头 if (!captureDevice) { NSLog(@"取得后置摄像头时出现问题."); return; } NSError *error=nil; //根据输入设备初始化设备输入对象,用于获得输入数据 _captureDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:captureDevice error:&error]; if (error) { NSLog(@"取得设备输入对象时出错,错误原因:%@",error.localizedDescription); return; } //初始化设备输出对象,用于获得输出数据 _captureStillImageOutput=[[AVCaptureStillImageOutput alloc]init]; NSDictionary *outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG}; [_captureStillImageOutput setOutputSettings:outputSettings];//输出设置 //将设备输入添加到会话中 if ([_captureSession canAddInput:_captureDeviceInput]) { [_captureSession addInput:_captureDeviceInput]; } //将设备输出添加到会话中 if ([_captureSession canAddOutput:_captureStillImageOutput]) { [_captureSession addOutput:_captureStillImageOutput]; } //创建视频预览层,用于实时展示摄像头状态 _captureVideoPreviewLayer=[[AVCaptureVideoPreviewLayer alloc]initWithSession:self.captureSession]; CALayer *layer=self.viewContainer.layer; layer.masksToBounds=YES; _captureVideoPreviewLayer.frame=layer.bounds; _captureVideoPreviewLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//填充模式 //将视频预览层添加到界面中 //[layer addSublayer:_captureVideoPreviewLayer]; [layer insertSublayer:_captureVideoPreviewLayer below:self.focusCursor.layer]; [self addNotificationToCaptureDevice:captureDevice]; [self addGenstureRecognizer]; [self setFlashModeButtonStatus]; } -(void)viewDidAppear:(BOOL)animated{ [super viewDidAppear:animated]; [self.captureSession startRunning]; } -(void)viewDidDisappear:(BOOL)animated{ [super viewDidDisappear:animated]; [self.captureSession stopRunning]; } -(void)dealloc{ [self removeNotification]; }
#pragma mark - UI方法 #pragma mark 拍照 - (IBAction)takeButtonClick:(UIButton *)sender { //根据设备输出获得连接 AVCaptureConnection *captureConnection=[self.captureStillImageOutput connectionWithMediaType:AVMediaTypeVideo]; //根据连接取得设备输出的数据 [self.captureStillImageOutput captureStillImageAsynchronouslyFromConnection:captureConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) { if (imageDataSampleBuffer) { NSData *imageData=[AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; UIImage *image=[UIImage imageWithData:imageData]; UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil); // ALAssetsLibrary *assetsLibrary=[[ALAssetsLibrary alloc]init]; // [assetsLibrary writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:nil]; } }]; } #pragma mark 切换前后摄像头 - (IBAction)toggleButtonClick:(UIButton *)sender { AVCaptureDevice *currentDevice=[self.captureDeviceInput device]; AVCaptureDevicePosition currentPosition=[currentDevice position]; [self removeNotificationFromCaptureDevice:currentDevice]; AVCaptureDevice *toChangeDevice; AVCaptureDevicePosition toChangePosition=AVCaptureDevicePositionFront; if (currentPosition==AVCaptureDevicePositionUnspecified||currentPosition==AVCaptureDevicePositionFront) { toChangePosition=AVCaptureDevicePositionBack; } toChangeDevice=[self getCameraDeviceWithPosition:toChangePosition]; [self addNotificationToCaptureDevice:toChangeDevice]; //获得要调整的设备输入对象 AVCaptureDeviceInput *toChangeDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:toChangeDevice error:nil]; //改变会话的配置前一定要先开启配置,配置完成后提交配置改变 [self.captureSession beginConfiguration]; //移除原有输入对象 [self.captureSession removeInput:self.captureDeviceInput]; //添加新的输入对象 if ([self.captureSession canAddInput:toChangeDeviceInput]) { [self.captureSession addInput:toChangeDeviceInput]; self.captureDeviceInput=toChangeDeviceInput; } //提交会话配置 [self.captureSession commitConfiguration]; [self setFlashModeButtonStatus]; }
#pragma mark 自动闪光灯开启 - (IBAction)flashAutoClick:(UIButton *)sender { [self setFlashMode:AVCaptureFlashModeAuto]; [self setFlashModeButtonStatus]; }
#pragma mark 打开闪光灯 - (IBAction)flashOnClick:(UIButton *)sender { [self setFlashMode:AVCaptureFlashModeOn]; [self setFlashModeButtonStatus]; }
#pragma mark 关闭闪光灯 - (IBAction)flashOffClick:(UIButton *)sender { [self setFlashMode:AVCaptureFlashModeOff]; [self setFlashModeButtonStatus]; }
#pragma mark - 通知 /** * 给输入设备添加通知 */ -(void)addNotificationToCaptureDevice:(AVCaptureDevice *)captureDevice{ //注意添加区域改变捕获通知必须首先设置设备允许捕获 [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) { captureDevice.subjectAreaChangeMonitoringEnabled=YES; }]; NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter]; //捕获区域发生改变 [notificationCenter addObserver:self selector:@selector(areaChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice]; }
/** * 移除所有通知 */ -(void)removeNotification{ NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter]; [notificationCenter removeObserver:self]; }
-(void)addNotificationToCaptureSession:(AVCaptureSession *)captureSession{ NSNotificationCenter *notificationCenter= [NSNotificationCenter defaultCenter]; //会话出错 [notificationCenter addObserver:self selector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotification object:captureSession]; }
/** * 设备连接成功 * * @param notification 通知对象 */ -(void)deviceConnected:(NSNotification *)notification{ NSLog(@"设备已连接..."); }
/** * 设备连接断开 * * @param notification 通知对象 */ -(void)deviceDisconnected:(NSNotification *)notification{ NSLog(@"设备已断开."); }
/** * 捕获区域改变 * * @param notification 通知对象 */ -(void)areaChange:(NSNotification *)notification{ NSLog(@"捕获区域改变..."); }
/** * 会话出错 * * @param notification 通知对象 */ -(void)sessionRuntimeError:(NSNotification *)notification{ NSLog(@"会话发生错误."); }
#pragma mark - 私有方法 /** * 取得指定位置的摄像头 * * @param position 摄像头位置 * * @return 摄像头设备 */ -(AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition )position{ NSArray *cameras= [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; for (AVCaptureDevice *camera in cameras) { if ([camera position]==position) { return camera; } } return nil; }
/** * 改变设备属性的统一操作方法 * * @param propertyChange 属性改变操作 */ -(void)changeDeviceProperty:(PropertyChangeBlock)propertyChange{ AVCaptureDevice *captureDevice= [self.captureDeviceInput device]; NSError *error; //注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁 if ([captureDevice lockForConfiguration:&error]) { propertyChange(captureDevice); [captureDevice unlockForConfiguration]; }else{ NSLog(@"设置设备属性过程发生错误,错误信息:%@",error.localizedDescription); } }
/** * 设置闪光灯模式 * * @param flashMode 闪光灯模式 */ -(void)setFlashMode:(AVCaptureFlashMode )flashMode{ [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) { if ([captureDevice isFlashModeSupported:flashMode]) { [captureDevice setFlashMode:flashMode]; } }]; }
/** * 设置聚焦模式 * * @param focusMode 聚焦模式 */ -(void)setFocusMode:(AVCaptureFocusMode )focusMode{ [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) { if ([captureDevice isFocusModeSupported:focusMode]) { [captureDevice setFocusMode:focusMode]; } }]; }
/** * 设置曝光模式 * * @param exposureMode 曝光模式 */ -(void)setExposureMode:(AVCaptureExposureMode)exposureMode{ [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) { if ([captureDevice isExposureModeSupported:exposureMode]) { [captureDevice setExposureMode:exposureMode]; } }]; }
/** * 设置聚焦点 * * @param point 聚焦点 */ -(void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point{ [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) { if ([captureDevice isFocusModeSupported:focusMode]) { [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus]; } if ([captureDevice isFocusPointOfInterestSupported]) { [captureDevice setFocusPointOfInterest:point]; } if ([captureDevice isExposureModeSupported:exposureMode]) { [captureDevice setExposureMode:AVCaptureExposureModeAutoExpose]; } if ([captureDevice isExposurePointOfInterestSupported]) { [captureDevice setExposurePointOfInterest:point]; } }]; }
/** * 添加点按手势,点按时聚焦 */ -(void)addGenstureRecognizer{ UITapGestureRecognizer *tapGesture=[[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(tapScreen:)]; [self.viewContainer addGestureRecognizer:tapGesture]; } -(void)tapScreen:(UITapGestureRecognizer *)tapGesture{ CGPoint point= [tapGesture locationInView:self.viewContainer]; //将UI坐标转化为摄像头坐标 CGPoint cameraPoint= [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point]; [self setFocusCursorWithPoint:point]; [self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint]; }
/** * 设置闪光灯按钮状态 */ -(void)setFlashModeButtonStatus{ AVCaptureDevice *captureDevice=[self.captureDeviceInput device]; AVCaptureFlashMode flashMode=captureDevice.flashMode; if([captureDevice isFlashAvailable]){ self.flashAutoButton.hidden=NO; self.flashOnButton.hidden=NO; self.flashOffButton.hidden=NO; self.flashAutoButton.enabled=YES; self.flashOnButton.enabled=YES; self.flashOffButton.enabled=YES; switch (flashMode) { case AVCaptureFlashModeAuto: self.flashAutoButton.enabled=NO; break; case AVCaptureFlashModeOn: self.flashOnButton.enabled=NO; break; case AVCaptureFlashModeOff: self.flashOffButton.enabled=NO; break; default: break; } }else{ self.flashAutoButton.hidden=YES; self.flashOnButton.hidden=YES; self.flashOffButton.hidden=YES; } }
/** * 设置聚焦光标位置 * * @param point 光标位置 */ -(void)setFocusCursorWithPoint:(CGPoint)point{ self.focusCursor.center=point; self.focusCursor.transform=CGAffineTransformMakeScale(1.5, 1.5); self.focusCursor.alpha=1.0; [UIView animateWithDuration:1.0 animations:^{ self.focusCursor.transform=CGAffineTransformIdentity; } completion:^(BOOL finished) { self.focusCursor.alpha=0; }];
关注博主微博每日更新技术:http://weibo.com/hanjunqiang
iOS中 自定义系统相机 作者:韩俊强的更多相关文章
- iOS中 MediaPlayer framework实现视频播放 韩俊强的博客
iOS开发中播放音乐可以使用MPMusicPlayerController类来实现,播放视频可以使用MPMoviePlayerController和MPMoviePlayerViewControlle ...
- iOS中 Realm错误总结整理 韩俊强的博客
每日更新关注:http://weibo.com/hanjunqiang 新浪微博! 一.错误信息:Attempting to modify object outside of a write tra ...
- iOS中 iOS10 权限崩溃问题 韩俊强的CSDN博客
iOS10 权限崩溃问题 每日更新关注:http://weibo.com/hanjunqiang 新浪微博! 今天 手机升级了 iOS10 Beta,然后用正在开发的项目 装了个ipa包,发现点击有 ...
- iOS中 动态热修补技术JSPatch 韩俊强的博客
.1.4) JSPatch bridge Objective-C and JavaScript. You can call any Objective-C class and method in Ja ...
- iOS中 最新支付宝支付(AliPay) 韩俊强的博客
每日更新关注:http://weibo.com/hanjunqiang 新浪微博 现在的支付方式一般有三种, 支付宝, 微信, 网银. 个人觉得最简单易用的还是支付宝, 微信虽然看起来币支付宝要简单 ...
- iOS中 断点下载详解 韩俊强的博客
布局如下: 基本拖拉属性: #import "ViewController.h" #import "AFNetworking.h" @interface Vie ...
- iOS中 百度地图详解 韩俊强的博文
需要准备工作按照下图引进类库 需要添加 添加的两个字符串为:NSLocationWhenInUseUsageDescription / NSLocationAlwaysUsageDescripti ...
- iOS中 图文混排/自定义图文混排 作者:韩俊强
指示根视图:(准备几张图片,把label加载在window上) CustomLable *label = [[CustomLable alloc]initWithFrame:CGRectMake(0, ...
- iOS中 MPMoviePlayer 实现视频音频播放 作者:韩俊强
ios播放视频文件一般使用 MPMoviePlayerViewController 和 MPMoviePlayerController.前者是一个view,后者是个Controller.区别就是 MP ...
随机推荐
- div,margin,padding
<!-- 类比礼品盒里装方块月饼.月饼的食用部分(我们把它称之为月饼肉身)要装在小包装盒里,月饼肉身即为content.月饼肉身与直接包裹它的小包装盒(我们把它叫做月饼的衣服)之间的距离叫pad ...
- form submit提交
form内控件参数自动添加到url后,而自定义的url参数则不能添加到url后 $('#fm').form('submit', { url: 'Data/Diary.ashx?dt=' + new D ...
- 干货满满,腾讯云+社区技术沙龙 Kafka Meetup 深圳站圆满结束
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 云+导语:4月22日,由腾讯云和 Kafka 社区主办.开源中国协办的腾讯云+社区技术沙龙 Kafka Meetup 深圳站在腾讯大厦举行, ...
- SpringMVC之简单的增删改查示例(SSM整合)
本篇文章主要介绍了SpringMVC之简单的增删改查示例(SSM整合),这个例子是基于SpringMVC+Spring+Mybatis实现的.有兴趣的可以了解一下. 虽然已经在做关于SpringMVC ...
- “你什么意思”之基于RNN的语义槽填充(Pytorch实现)
1. 概况 1.1 任务 口语理解(Spoken Language Understanding, SLU)作为语音识别与自然语言处理之间的一个新兴领域,其目的是为了让计算机从用户的讲话中理解他们的意图 ...
- 实验-使用VisualVM或JConsole进行对程序进行性能分析
参考资料: 性能分析神器VisualVM java可视化监控工具 完成下列任务: 1.分析内存堆 使用+进行频繁的字符串拼接 2.CPU性能分析 3.线程分析 编程比较以下几个方法所创建的线程 Exe ...
- springMVC源码分析--访问请求执行ServletInvocableHandlerMethod和InvocableHandlerMethod
在之前一篇博客中springMVC源码分析--RequestMappingHandlerAdapter(五)我们已经简单的介绍到具体请求访问的执行某个Controller中的方法是在RequestMa ...
- 不可错过的Node.js框架
前言 Node.js是由Ryan Dahl于2009年创建的.它是一个开源的跨平台运行时环境,用于开发服务器端和网络应用程序,它是基于Google Chrome V8 JavaScript引擎构建的. ...
- Lua热更新(hotfix)
Lua热更新(hotfix)(金庆的专栏)hotfixLua 5.2/5.3 hotfix. Hot update functions and keep old data.https://github ...
- SpriteKit物理引擎碰撞中5个重要信息
我们知道在SpriteKit物理引擎实际是基于Box2D! 在SpriteKit中当你设置好适当的碰撞参数后,通过遵守SKPhysicsContactDelegate,你可以选择实现2各碰撞回调方法: ...