首先导入一个头文件

    #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. ubuntu环境下vmware取消自动启动服务

    概述其实vmware这个服务取不取消,影响不大,主要是我有强迫症,在不用虚拟机的时候,看着vmware占着进程真心不爽,想要解决这个问题,在用虚拟机的时候启动服务,反之,则停.接下来,我说一下实现吧. ...

  2. iOS Block理解

    以前看到Block觉得也没什么,不就是类似函数的东西,这东西在C#里就是委托,在Java里就是块,有什么稀奇的.但看到一点进阶的内容后,发现这个东西确实有用. 所以做下总结. 一.块的基本用法 块的语 ...

  3. 2013年度IT博客大赛跻身10强

    2013年12月26日,由51CTO独家举办的2013年度IT博客大赛圆满落幕,荣幸跻身10强[http://fellow.51cto.com/art/201312/425528.htm],首先感谢各 ...

  4. 在Visual Studio上开发Node.js程序(2)——远程调试及发布到Azure

    [题外话] 上次介绍了VS上开发Node.js的插件Node.js Tools for Visual Studio(NTVS),其提供了非常方便的开发和调试功能,当然很多情况下由于平台限制等原因需要在 ...

  5. 从零3D基础入门XNA 4.0(2)——模型和BasicEffect

    [题外话] 上一篇文章介绍了3D开发基础与XNA开发程序的整体结构,以及使用Model类的Draw方法将模型绘制到屏幕上.本文接着上一篇文章继续,介绍XNA中模型的结构.BasicEffect的使用以 ...

  6. [ASP.NET MVC 小牛之路]05 - 使用 Ninject

    在[ASP.NET MVC 小牛之路]系列上一篇文章(依赖注入(DI)和Ninject)的末尾提到了在ASP.NET MVC中使用Ninject要做的两件事情,续这篇文章之后,本文将用一个实际的示例来 ...

  7. TODO:小程序手机预览调试

    TODO:小程序手机预览调试 1. 小程序注册,目前还未开通个人注册,主体类型为企业.政府.媒体.其他组织 2. 登录小程序,绑定开发者,获取AppID 3. 下载微信小程序示例-新片预告 https ...

  8. 《FaceBook效应》——读后总结

    这本书讲述了facebook从如何创建.到风靡全球,并结合facebook的网络效应讲述为什么facebook可以做到社交龙头.读这本书的时候,也可以看看<社交网络>这部电影. faceb ...

  9. JVM字节码指令

    invokevirtual 调用实例方法 invokespecial 调用父类构造,实例初始化方法,私有方法 dup 复制栈顶数值,并且复制值进栈,pop/pop2为栈顶值出栈 aload_0 加载第 ...

  10. 兼容各浏览器的js判断上传文件大小

    由于项目需要,在网上找了一个JS判断上传文件大小的程序,经测试兼容IE6-,Firefox10,Opera11.,safari5.,chrome17 <!DOCTYPE html> < ...