30分钟搞定iOS自定义相机

移动开发 iOS
最近公司的项目中用到了相机,由于不用系统的相机,UI给的相机切图,必须自定义才可以。就花时间简单研究了一下相机的自定义。

最近公司的项目中用到了相机,由于不用系统的相机,UI给的相机切图,必须自定义才可以。就花时间简单研究了一下相机的自定义。

相机属于系统硬件,这就需要我们来手动调用iPhone的相机硬件,分为以下步骤:

1、首先声明以下对象

  1. #import <AVFoundation/AVFoundation.h> 
  2. //捕获设备,通常是前置摄像头,后置摄像头,麦克风(音频输入)  
  3. @property (nonatomic, strong) AVCaptureDevice *device;  
  4.  
  5. //AVCaptureDeviceInput 代表输入设备,他使用AVCaptureDevice 来初始化  
  6. @property (nonatomic, strong) AVCaptureDeviceInput *input;  
  7.  
  8. //输出图片  
  9. @property (nonatomic ,strong) AVCaptureStillImageOutput *imageOutput;  
  10.  
  11. //session:由他把输入输出结合在一起,并开始启动捕获设备(摄像头)  
  12. @property (nonatomic, strong) AVCaptureSession *session;  
  13.  
  14. //图像预览层,实时显示捕获的图像  
  15. @property (nonatomic ,strong) AVCaptureVideoPreviewLayer *previewLayer; 

2、初始化各个对象

  1. - (void)cameraDistrict  
  2. {  
  3. //    AVCaptureDevicePositionBack  后置摄像头  
  4. //    AVCaptureDevicePositionFront 前置摄像头  
  5.    self.device = [self cameraWithPosition:AVCaptureDevicePositionFront];  
  6.     self.input = [[AVCaptureDeviceInput alloc] initWithDevice:self.device error:nil];  
  7.  
  8.     self.imageOutput = [[AVCaptureStillImageOutput alloc] init];  
  9.  
  10.     self.session = [[AVCaptureSession alloc] init];  
  11.     //     拿到的图像的大小可以自行设定  
  12.     //    AVCaptureSessionPreset320x240  
  13.     //    AVCaptureSessionPreset352x288  
  14.     //    AVCaptureSessionPreset640x480  
  15.     //    AVCaptureSessionPreset960x540  
  16.     //    AVCaptureSessionPreset1280x720  
  17.     //    AVCaptureSessionPreset1920x1080  
  18.     //    AVCaptureSessionPreset3840x2160  
  19.     self.session.sessionPreset = AVCaptureSessionPreset640x480;  
  20.     //输入输出设备结合  
  21.     if ([self.session canAddInput:self.input]) {  
  22.         [self.session addInput:self.input];  
  23.     }  
  24.     if ([self.session canAddOutput:self.imageOutput]) {  
  25.         [self.session addOutput:self.imageOutput];  
  26.     }  
  27.     //预览层的生成  
  28.     self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];  
  29.     self.previewLayer.frame = CGRectMake(0, 64, SCREEN_WIDTH, SCREEN_HEIGHT-64);  
  30.     self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;  
  31.     [self.view.layer addSublayer:self.previewLayer];  
  32.     //设备取景开始  
  33.     [self.session startRunning];  
  34.     if ([_device lockForConfiguration:nil]) {  
  35.     //自动闪光灯,  
  36.         if ([_device isFlashModeSupported:AVCaptureFlashModeAuto]) {  
  37.             [_device setFlashMode:AVCaptureFlashModeAuto];  
  38.         }  
  39.         //自动白平衡,但是好像一直都进不去  
  40.         if ([_device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeAutoWhiteBalance]) {  
  41.             [_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeAutoWhiteBalance];  
  42.         }  
  43.         [_device unlockForConfiguration];  
  44.     }  
  45.  

根据前后置位置拿到相应的摄像头:

  1. - (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position{  
  2.     NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];  
  3.     for ( AVCaptureDevice *device in devices )  
  4.         if ( device.position == position ){  
  5.             return device;  
  6.         }  
  7.     return nil;  

3、拍照拿到相应图片:

  1. - (void)photoBtnDidClick  
  2. {  
  3.     AVCaptureConnection *conntion = [self.imageOutput connectionWithMediaType:AVMediaTypeVideo];  
  4.       if (!conntion) {  
  5.           NSLog(@"拍照失败!");  
  6.           return;  
  7.           }  
  8.     [self.imageOutput captureStillImageAsynchronouslyFromConnection:conntion completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {  
  9.         if (imageDataSampleBuffer == nil) {  
  10.             return ;  
  11.           }  
  12.         NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];  
  13.         self.image = [UIImage imageWithData:imageData];  
  14.         [self.session stopRunning];  
  15.         [self.view addSubview:self.cameraImageView];  

4、前后置摄像头的切换

  1. - (void)changeCamera{  
  2.     NSUInteger cameraCount = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count];  
  3.     if (cameraCount > 1) {  
  4.         NSError *error;  
  5.         //给摄像头的切换添加翻转动画  
  6.         CATransition *animation = [CATransition animation];  
  7.         animation.duration = .5f;  
  8.         animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];  
  9.         animation.type = @"oglFlip";  
  10.  
  11.         AVCaptureDevice *newCamera = nil;  
  12.         AVCaptureDeviceInput *newInput = nil;  
  13.   //拿到另外一个摄像头位置  
  14.         AVCaptureDevicePosition position = [[_input device] position];  
  15.         if (position == AVCaptureDevicePositionFront){  
  16.             newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];  
  17.             animation.subtype = kCATransitionFromLeft;//动画翻转方向  
  18.         }  
  19.         else {  
  20.             newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];  
  21.             animation.subtype = kCATransitionFromRight;//动画翻转方向  
  22.         }  
  23.         //生成新的输入  
  24.         newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];  
  25.         [self.previewLayer addAnimation:animation forKey:nil];  
  26.         if (newInput != nil) {  
  27.             [self.session beginConfiguration];  
  28.             [self.session removeInput:self.input];  
  29.             if ([self.session canAddInput:newInput]) {  
  30.                 [self.session addInput:newInput];  
  31.                 self.input = newInput;  
  32.  
  33.             } else {  
  34.                 [self.session addInput:self.input];  
  35.             }  
  36.             [self.session commitConfiguration];  
  37.  
  38.         } else if (error) {  
  39.             NSLog(@"toggle carema failed, error = %@", error);  
  40.         }  
  41.     }  

5、相机的其它参数设置

  1. //AVCaptureFlashMode  闪光灯  
  2. //AVCaptureFocusMode  对焦  
  3. //AVCaptureExposureMode  曝光  
  4. //AVCaptureWhiteBalanceMode  白平衡  
  5. //闪光灯和白平衡可以在生成相机时候设置  
  6. //曝光要根据对焦点的光线状况而决定,所以和对焦一块写  
  7. //point为点击的位置  
  8. - (void)focusAtPoint:(CGPoint)point{  
  9.     CGSize size = self.view.bounds.size;  
  10.     CGPoint focusPoint = CGPointMake( point.y /size.height ,1-point.x/size.width );  
  11.     NSError *error;  
  12.     if ([self.device lockForConfiguration:&error]) {  
  13.         //对焦模式和对焦点  
  14.         if ([self.device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {  
  15.             [self.device setFocusPointOfInterest:focusPoint];  
  16.             [self.device setFocusMode:AVCaptureFocusModeAutoFocus];  
  17.         }  
  18.         //曝光模式和曝光点  
  19.         if ([self.device isExposureModeSupported:AVCaptureExposureModeAutoExpose ]) {  
  20.             [self.device setExposurePointOfInterest:focusPoint];  
  21.             [self.device setExposureMode:AVCaptureExposureModeAutoExpose];  
  22.         }  
  23.  
  24.         [self.device unlockForConfiguration];  
  25.         //设置对焦动画  
  26.         _focusView.center = point;  
  27.         _focusView.hidden = NO;  
  28.         [UIView animateWithDuration:0.3 animations:^{  
  29.             _focusView.transform = CGAffineTransformMakeScale(1.25, 1.25);  
  30.         }completion:^(BOOL finished) {  
  31.             [UIView animateWithDuration:0.5 animations:^{  
  32.                 _focusView.transform = CGAffineTransformIdentity;  
  33.             } completion:^(BOOL finished) {  
  34.                 _focusView.hidden = YES;  
  35.             }];  
  36.         }];  
  37.     }  
  38.  

6、遇到的一些坑和解决办法

1) 前后置摄像头的切换

前后值不能切换,各种尝试找了半天没找到有原因。后来发现我在设置图片尺寸的时候设置为1080P [self.session canSetSessionPreset: AVCaptureSessionPreset1920x1080] ,前置摄像头并不支持这么大的尺寸,所以就不能切换前置摄像头。我验证了下 前置摄像头最高支持720P,720P以内可自由切换。

当然也可以在前后置摄像头切换的时候,根据前后摄像头来设置不同的尺寸,这里不在赘述。

2)焦点位置

CGPoint focusPoint = CGPointMake( point.y /size.height ,1-point.x/size.width );

setExposurePointOfInterest:focusPoint 函数后面Point取值范围是取景框左上角(0,0)到取景框右下角(1,1)之间。官方是这么写的:

The value of this property is a CGPoint that determines the receiver's focus point of interest, if it has one. A value of (0,0) indicates that the camera should focus on the top left corner of the image, while a value of (1,1) indicates that it should focus on the bottom right. The default value is (0.5,0.5).

我也试了按这个来但位置就是不对,只能按上面的写法才可以。前面是点击位置的y/PreviewLayer的高度,后面是1-点击位置的x/PreviewLayer的宽度

3)对焦和曝光

我在设置对焦是 先设置了模式setFocusMode,后设置对焦位置,就会导致很奇怪的现象,对焦位置是你上次点击的位置。所以一定要先设置位置,再设置对焦模式。

曝光同上

7、写在最后

附上demo:https://github.com/nanshanyi/photographDemo

常用到的基本就这么多,写的并不完善,有什么不对的,欢迎大家批评指正,共同学习。

责任编辑:陈琳 来源: 简书
相关推荐

2017-01-10 09:07:53

tcpdumpGET请求

2013-05-03 10:57:09

泛型泛型教程

2020-05-22 10:20:27

Shiro架构字符串

2021-12-01 06:50:50

Docker底层原理

2015-01-14 15:06:48

定义相机

2022-03-11 09:10:10

hooksReact自定义

2011-05-26 09:03:17

JSONjavascript

2009-11-26 11:19:52

NIS服务器

2018-03-20 16:46:03

Windows 10Windows部署更新

2017-07-18 11:10:45

2010-03-05 17:28:08

2011-02-21 17:48:35

vsFTPd

2013-06-24 15:32:41

JPush极光推送Android Pus移动开发

2017-09-27 11:00:50

LinuxBash使用技巧

2023-04-12 11:18:51

甘特图前端

2017-06-07 18:40:33

PromiseJavascript前端

2024-08-27 13:43:38

Spring系统业务

2013-12-11 10:00:14

C++新特性C

2020-12-18 07:33:20

SpringSchedule组件

2023-11-30 10:21:48

虚拟列表虚拟列表工具库
点赞
收藏

51CTO技术栈公众号