首先导入一个头文件

    #import <AVFoundation/AVFoundation.h>

由于后面我们需要将拍摄好的照片写入系统相册中,所以我们在这里还需要导入一个相册需要的头文件

   #import <AssetsLibrary/AssetsLibrary.h>

导入头文件后我们需要创建几个相机必须的属性

    /**
* AVCaptureSession对象来执行输入设备和输出设备之间的数据传递
*/
@property (nonatomic, strong) AVCaptureSession* session;
/**
* 输入设备
*/
@property (nonatomic, strong) AVCaptureDeviceInput* videoInput;
/**
* 照片输出流
*/
@property (nonatomic, strong) AVCaptureStillImageOutput* stillImageOutput;
/**
* 预览图层
*/
@property (nonatomic, strong) AVCaptureVideoPreviewLayer* previewLayer;

AVCaptureSession控制输入和输出设备之间的数据传递
AVCaptureDeviceInput调用所有的输入硬件。例如摄像头和麦克风
AVCaptureStillImageOutput用于输出图像
AVCaptureVideoPreviewLayer镜头捕捉到得预览图层

一个session可以管理多个输入输出设备,如下图所示

输入输出设备之间的关系(图片来自苹果官方开发文档)

接下来初始化所有对象,下面这个方法的调用我放到viewDidLoad里面调用了

- (void)initAVCaptureSession{

self.session = [[AVCaptureSession alloc] init];

NSError *error;

AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

//更改这个设置的时候必须先锁定设备,修改完后再解锁,否则崩溃
[device lockForConfiguration:nil];
//设置闪光灯为自动
[device setFlashMode:AVCaptureFlashModeAuto];
[device unlockForConfiguration]; self.videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
if (error) {
NSLog(@"%@",error);
}
self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
//输出设置。AVVideoCodecJPEG 输出jpeg格式图片
NSDictionary * outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey, nil];
[self.stillImageOutput setOutputSettings:outputSettings]; if ([self.session canAddInput:self.videoInput]) {
[self.session addInput:self.videoInput];
}
if ([self.session canAddOutput:self.stillImageOutput]) {
[self.session addOutput:self.stillImageOutput];
} //初始化预览图层
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
[self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
NSLog(@"%f",kMainScreenWidth);
self.previewLayer.frame = CGRectMake(0, 0,kMainScreenWidth, kMainScreenHeight - 64);
self.backView.layer.masksToBounds = YES;
[self.backView.layer addSublayer:self.previewLayer];
}

之后在viewWillAppear,viewDidDisappear方法里开启和关闭session

- (void)viewWillAppear:(BOOL)animated{

    [super viewWillAppear:YES];

    if (self.session) {

    [self.session startRunning];
}
} - (void)viewDidDisappear:(BOOL)animated{ [super viewDidDisappear:YES]; if (self.session) { [self.session stopRunning];
}
}

到这里所有的初始化工作基本完成,运行程序可以看到镜头捕捉到得画面。接下来实现拍照按钮。

输出图像的时候需要用到AVCaptureConnection这个类,session通过AVCaptureConnection连接AVCaptureStillImageOutput进行图片输出,输入输出与connection的关系如下图

图片来自苹果官方开发文档

接下来搞一个获取设备方向的方法,再配置图片输出的时候需要使用

 -(AVCaptureVideoOrientation)avOrientationForDeviceOrientation:(UIDeviceOrientation)deviceOrientation
{
AVCaptureVideoOrientation result = (AVCaptureVideoOrientation)deviceOrientation;
if ( deviceOrientation == UIDeviceOrientationLandscapeLeft )
result = AVCaptureVideoOrientationLandscapeRight;
else if ( deviceOrientation == UIDeviceOrientationLandscapeRight )
result = AVCaptureVideoOrientationLandscapeLeft;
return result;
}

下面是拍照按钮方法

 - (IBAction)takePhotoButtonClick:(UIBarButtonItem *)sender {

    AVCaptureConnection *stillImageConnection = [self.stillImageOutput        connectionWithMediaType:AVMediaTypeVideo];
UIDeviceOrientation curDeviceOrientation = [[UIDevice currentDevice] orientation];
AVCaptureVideoOrientation avcaptureOrientation = [self avOrientationForDeviceOrientation:curDeviceOrientation];
[stillImageConnection setVideoOrientation:avcaptureOrientation];
[stillImageConnection setVideoScaleAndCropFactor:1]; [self.stillImageOutput captureStillImageAsynchronouslyFromConnection:stillImageConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) { NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault,
imageDataSampleBuffer,
kCMAttachmentMode_ShouldPropagate); ALAuthorizationStatus author = [ALAssetsLibrary authorizationStatus];
if (author == ALAuthorizationStatusRestricted || author == ALAuthorizationStatusDenied){
//无权限
return ;
}
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
[library writeImageDataToSavedPhotosAlbum:jpegData metadata:(__bridge id)attachments completionBlock:^(NSURL *assetURL, NSError *error) { }]; }];
}

至此相机的拍照功能已经完成
注:

  • [stillImageConnection setVideoScaleAndCropFactor:1];这个方法是控制焦距用的暂时先固定为1,后边写手势缩放焦距的时候会修改这里
  • 照片写入相册之前需要进行旋转(我在代码里并没有进行旋转)
  • 写入相册之前需要判断用户是否允许了程序访问相册,否则程序会崩溃,包括在开启相机的时候和拍摄按钮点击的时候都需要做安全验证,验证设别是否支持拍照,用户是否允许程序访问相机。

接下来完成闪光灯

- (IBAction)flashButtonClick:(UIBarButtonItem *)sender {

   NSLog(@"flashButtonClick");

   AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

   //修改前必须先锁定
[device lockForConfiguration:nil];
//必须判定是否有闪光灯,否则如果没有闪光灯会崩溃
if ([device hasFlash]) { if (device.flashMode == AVCaptureFlashModeOff) {
device.flashMode = AVCaptureFlashModeOn; [sender setTitle:@"flashOn"];
} else if (device.flashMode == AVCaptureFlashModeOn) {
device.flashMode = AVCaptureFlashModeAuto;
[sender setTitle:@"flashAuto"];
} else if (device.flashMode == AVCaptureFlashModeAuto) {
device.flashMode = AVCaptureFlashModeOff;
[sender setTitle:@"flashOff"];
} } else { NSLog(@"设备不支持闪光灯");
}
[device unlockForConfiguration];
}

闪光灯的设置非常简单,只需要修改device的flashMode属性即可,这里需要注意的是,修改device时候需要先锁住,修改完成后再解锁,否则会崩溃,设置闪光灯的时候也需要做安全判断,验证设备是否支持闪光灯,有些iOS设备是没有闪光灯的,如果不做判断还是会crash掉 T_T

剩下一个小功能就是切回镜头了,方法如下

- (IBAction)switchCameraSegmentedControlClick:(UISegmentedControl *)sender {

     NSLog(@"%ld",(long)sender.selectedSegmentIndex);

    AVCaptureDevicePosition desiredPosition;
if (isUsingFrontFacingCamera){
desiredPosition = AVCaptureDevicePositionBack;
}else{
desiredPosition = AVCaptureDevicePositionFront;
} for (AVCaptureDevice *d in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]) {
if ([d position] == desiredPosition) {
[self.previewLayer.session beginConfiguration];
AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:d error:nil];
for (AVCaptureInput *oldInput in self.previewLayer.session.inputs) {
[[self.previewLayer session] removeInput:oldInput];
}
[self.previewLayer.session addInput:input];
[self.previewLayer.session commitConfiguration];
break;
}
} isUsingFrontFacingCamera = !isUsingFrontFacingCamera;
}

isUsingFrontFacingCamera这个属性是个BOOL值变量,前面忘记写这个属性了。用于防止重复切换统一摄像头,调用这个点击方法的控件是个segement,文章最后我会附上demo地址。

最后一步就是加入手势缩放,手动调节相机焦距。
加入两个属性,并遵守这个协议<UIGestureRecognizerDelegate>

      /**
* 记录开始的缩放比例
*/
@property(nonatomic,assign)CGFloat beginGestureScale;
/**
* 最后的缩放比例
*/
@property(nonatomic,assign)CGFloat effectiveScale;

这两个属性分别用于记录缩放的比例。相机支持的焦距是1.0~67.5,所以再控制器加载的时候分别给这两个属性附上一个初值 1.0。之后给view添加一个缩放手势,手势调用的方法如下

//缩放手势 用于调整焦距
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)recognizer{ BOOL allTouchesAreOnThePreviewLayer = YES;
NSUInteger numTouches = [recognizer numberOfTouches], i;
for ( i = 0; i < numTouches; ++i ) {
CGPoint location = [recognizer locationOfTouch:i inView:self.backView];
CGPoint convertedLocation = [self.previewLayer convertPoint:location fromLayer:self.previewLayer.superlayer];
if ( ! [self.previewLayer containsPoint:convertedLocation] ) {
allTouchesAreOnThePreviewLayer = NO;
break;
}
} if ( allTouchesAreOnThePreviewLayer ) { self.effectiveScale = self.beginGestureScale * recognizer.scale;
if (self.effectiveScale < 1.0){
self.effectiveScale = 1.0;
} NSLog(@"%f-------------->%f------------recognizerScale%f",self.effectiveScale,self.beginGestureScale,recognizer.scale); CGFloat maxScaleAndCropFactor = [[self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo] videoMaxScaleAndCropFactor]; NSLog(@"%f",maxScaleAndCropFactor);
if (self.effectiveScale > maxScaleAndCropFactor)
self.effectiveScale = maxScaleAndCropFactor; [CATransaction begin];
[CATransaction setAnimationDuration:.025];
[self.previewLayer setAffineTransform:CGAffineTransformMakeScale(self.effectiveScale, self.effectiveScale)];
[CATransaction commit]; } }

这样之再实现一个delegate

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ( [gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]] ) {
self.beginGestureScale = self.effectiveScale;
}
return YES;
}

在每次手势开始的时候把上一次实际缩放值赋给初始缩放值,如果不这么做的话你会发现每次手势开始的时候界面都会跳来跳去的(非常性感)。一个简单功能的相机基本上完成了,最后一步就是之前我们在拍照的方法里写死了一个1.0,我们还需要修改一下它,,否则虽然你看到的界面焦距改变了,但是实际拍出来的照片是没有变化的。找到拍照方法里的

[stillImageConnection setVideoScaleAndCropFactor:1.0];

修改为

[stillImageConnection setVideoScaleAndCropFactor:self.effectiveScale];

至此大功告成。

文章演示demo下载
(https://github.com/RockyAo/RACustomCamera)

苹果官方演示demo下载
(https://github.com/RockyAo/CameraDemo)

官方的演示demo里面还有个面部识别。

最后如果你想监听相机的对焦事件的话
再viewWillApper里面添加个监听

AVCaptureDevice *camDevice =[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
int flags =NSKeyValueObservingOptionNew;
[camDevice addObserver:self forKeyPath:@"adjustingFocus" options:flags context:nil];

然后实现下面方法

-(void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context {..........//在这里做你想做的事~~~}

最后别忘了再viewDidDisapper方法里移除监听

AVCaptureDevice*camDevice =[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
[camDevice removeObserver:self forKeyPath:@"adjustingFocus"];

第一次写东西哈,写的不好见谅有错误请指出。~~~

iOS开发--AVFoundation自定义相机的更多相关文章

  1. iOS开发之自定义表情键盘(组件封装与自动布局)

    下面的东西是编写自定义的表情键盘,话不多说,开门见山吧!下面主要用到的知识有MVC, iOS开发中的自动布局,自定义组件的封装与使用,Block回调,CoreData的使用.有的小伙伴可能会问写一个自 ...

  2. 详解iOS开发之自定义View

    iOS开发之自定义View是本文要将介绍的内容,iOS SDK中的View是UIView,我们可以很方便的自定义一个View.创建一个 Window-based Application程序,在其中添加 ...

  3. 【Swift】IOS开发中自定义转场动画

    在IOS开发中,我们model另外一个控制器的时候,一般都使用默认的转场动画. 其实我们可以自定义一些转场动画.达到不同的转场效果. 步骤如下:(photoBrowser是目标控制器) 1.在源控制器 ...

  4. iOS使用AVCaptureSession自定义相机

    关于iOS调用摄像机来获取照片,通常我们都会调用UIImagePickerController来调用系统提供的相机来拍照,这个控件非常好 用.但是有时UIImagePickerController控件 ...

  5. 用AVFoundation自定义相机拍照

    自定义拍照或者录视频的功能,就需要用到AVFoundation框架,目前我只用到了拍照,所以记录下自定义拍照用法,视频用法等用上了再补充,应该是大同小异 demo在这里:https://github. ...

  6. iOS开发-UITableView自定义Cell

    UITableView在iOS中开发的重要地位是毋庸置疑的,基本上应用中用到的比例是一半左右,而且大部分情况都是需要自定义单元格的,这样用户看到的App才能更有美感.之前写过UITableView的基 ...

  7. iOS开发小技巧--相机相册的正确打开方式

    iOS相机相册的正确打开方式- UIImagePickerController 通过指定sourceType来实现打开相册还是相机 UIImagePickerControllerSourceTypeP ...

  8. IOS开发之自定义系统弹出键盘上方的view(转载)

    这篇文章解决的一个开发中的实际问题就是:当弹出键盘时,自定义键盘上方的view.目前就我的经验来看,有两种解决方法.一个就是利用UITextField或者UITextView的inputAccesso ...

  9. iOS开发之自定义导航栏返回按钮右滑返回手势失效的解决

    我相信针对每一个iOS开发者来说~除了根视图控制器外~所有的界面通过导航栏push过去的界面都是可以通过右滑来返回上一个界面~其实~在很多应用和APP中~用户已经习惯了这个功能~然而~作为开发者的我们 ...

随机推荐

  1. php获取网卡MAC地址源码

    <?php /** 获取网卡的MAC地址原码:目前支持WIN/LINUX系统 获取机器网卡的物理(MAC)地址 **/ class GetMacAddr{ var $return_array = ...

  2. 解决:No module named pkg_resources

    今天在装bleach的时候,发现bleach依赖的其中的一个库是html5lib,从pypi下载源码包,执行python setup.py install,报错了: 提示我的setuptools版本过 ...

  3. nginx-(/usr/local/nginx)配置编译

    ./configure \ --prefix=/usr/local/nginx \ --sbin-path=/usr/local/nginx/sbin/nginx \ --conf-path=/usr ...

  4. 比achartengine更加强大的Android图表控件。

    比achartengine更加强大的图表控件MPAndroidChart. 详细使用及demo:http://www.see-source.com/androidwidget/detail.html? ...

  5. sql查询

    结果集是 id name 1 张三2 张三3 李四4 王五5 王五我想查询出有多少不重名的人的数量,并显示在每一行结果集里面结果如下:num id name 3     1   张三3     2  ...

  6. NLP常用工具

    1.统计类工具:可参见[统计学习常用Python扩展包] 2.linux自带工具:可参见[[整理]Linux常用文本处理命令] 3.简繁转换工具:opencc Open Chinese Convert ...

  7. SQL Server 2016 CTP2.2 的关键特性

    SQL Server 2016 CTP2.2 的关键特性 正如微软CEO 说的,SQL Server2016 是一个Breakthrough Flagship  Database(突破性的旗舰级数据库 ...

  8. ASP.NET vNext 概述

    兼容Mono的下一代云环境Web开发框架ASP.NET vNext 我们知道了ASP.NET vNext是一个全新的框架,是一个与时俱进的框架.这篇文章将深入讨论在整体架构更多的细节,文档参照 ASP ...

  9. SQL Server 重新组织生成索引

    标签:SQL SERVER/MSSQL SERVER/数据库/DBA/索引/统计信息 概述 无论何时对基础数据执行插入.更新或删除操作,SQL Server 数据库引擎都会自动维护索引.随着时间的推移 ...

  10. 七天学会ASP.NET MVC (二)——ASP.NET MVC 数据传递

    通过第一天的学习之后,我们相信您已经对MVC有一些基本了解. 本节所讲的内容是在上节的基础之上,因此需要确保您是否掌握了上一节的内容.本章的目标是在今天学习结束时利用最佳实践解决方案创建一个小型的MV ...