项目中的拍照识别题目功能,使用了自定义相机,现将其记录如下:
.h中
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
NS_ASSUME_NONNULL_BEGIN
@class CameraView;
@protocol CameraViewDelegate <NSObject>
@optional;
/// 闪光灯
- (void)torchLightAction:(CameraView *)cameraView handle:(void(^)(NSError *error))handle;
/// 聚焦
- (void)focusAction:(CameraView *)cameraView point:(CGPoint)point handle:(void(^)(NSError *error))handle;
/// 取消
- (void)cancelAction:(CameraView *)cameraView;
/// 拍照
- (void)takePhotoAction:(CameraView *)cameraView withImage:(UIImage *)image;
/// 相册按钮点击
- (void)albumAction:(CameraView *)cameraView;
@end
@interface CameraView : UIView
@property(nonatomic, weak) id <CameraViewDelegate> delegate;
@property (nonatomic, strong) AVCaptureDeviceInput *deviceInput;
@property (nonatomic, strong) UIButton *pictureButton; //相册按钮
@property (nonatomic, strong) UIButton *flashButton; //闪光灯按钮
@property (nonatomic, strong) UIButton *takePhotoButton;//拍照按钮
@property (nonatomic, strong) UIButton *backButton; //关闭按钮
@property (nonatomic, strong) UILabel *scriptLabel; //描述文字
- (void)startCamera;
- (void)stopCamera;
@end
.m中:
#import "CameraView.h"
#import "CameraPreView.h"
#import "CameraManager.h"
#import "FocusView.h"
#define MAINSCREEN_BOUNDS [UIScreen mainScreen].bounds
typedef void(^PropertyChangeBlock)(AVCaptureDevice *captureDevice);
@interface CameraView ()<AVCapturePhotoCaptureDelegate>
// AVFoundation 的属性
@property (nonatomic, strong) AVCaptureSession *captureSession;
@property (nonatomic, strong) AVCaptureStillImageOutput *imageOutput;
@property (nonatomic, strong) AVCaptureDevice *captureDevice;
@property (nonatomic, strong) CameraPreView *previewView;
@property (nonatomic, retain) AVCapturePhotoSettings *outputSettings;
@property (nonatomic, strong) FocusView *focusView;
@end
@implementation CameraView
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setUpCameraPreviewLayer];
// 花线
[self setLineView];
[self configureUI];
[self addGenstureRecognizer];
}
return self;
}
#pragma mark - AVFoundation 配置
- (void)setUpCameraPreviewLayer {
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (authStatus == AVAuthorizationStatusRestricted || authStatus == AVAuthorizationStatusDenied) {
return;
}
if (!self.previewView) {
[self initSession];
self.previewView = [[CameraPreView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight)];
[self addSubview:self.previewView];
self.previewView.captureSession = self.captureSession;
}
}
- (void)initSession {
self.captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
self.captureSession = [[AVCaptureSession alloc] init];
self.captureSession.sessionPreset = AVCaptureSessionPresetHigh;
self.deviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:[self cameraWithDirection:AVCaptureDevicePositionBack] error:nil];
// 静态图片输出
self.imageOutput = [[AVCaptureStillImageOutput alloc] init];
self.imageOutput.outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG};
if ([self.captureSession canAddInput:self.deviceInput]) {
[self.captureSession addInput:self.deviceInput];
}
if ([self.captureSession canAddOutput:self.imageOutput]) {
[self.captureSession addOutput:self.imageOutput];
}
//注意添加区域改变捕获通知必须首先设置设备允许捕获
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
self.captureDevice.subjectAreaChangeMonitoringEnabled = YES;
}];
}
- (AVCaptureDevice *)cameraWithDirection:(AVCaptureDevicePosition)position {
// NSArray *devices = [AVCaptureDevice AVCaptureDeviceDiscoverySession:AVMediaTypeVideo];
// for (AVCaptureDevice *device in devices) {
// if ([device position] == position) {
// return device;
// }
// }
// return nil;
AVCaptureDeviceDiscoverySession *devicesIOS10 = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:@[AVCaptureDeviceTypeBuiltInWideAngleCamera] mediaType:AVMediaTypeVideo position:position];
NSArray *devicesIOS = devicesIOS10.devices;
for (AVCaptureDevice *device in devicesIOS) {
if ([device position] == position) {
return device;
}
}
return nil;
}
//通过给屏幕上的view添加手势,获取手势的坐标.将坐标用setFocusPointOfInterest方法赋值给device
-(void)changeDeviceProperty:(PropertyChangeBlock)propertyChange{
AVCaptureDevice *captureDevice= [self.deviceInput device];
NSError *error;
//注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁
if ([captureDevice lockForConfiguration:&error]) {
propertyChange(captureDevice);
[captureDevice unlockForConfiguration];
}else{
NSLog(@"设置设备属性过程发生错误,错误信息:%@",error.localizedDescription);
}
}
#pragma mark - ConfigureUI
// 线条
- (void)setLineView{
for (int i=1; i<3; i++) {
UIView *lineView1 = [[UIView alloc]init];
lineView1.frame = CGRectMake(MAINSCREEN_BOUNDS.size.width/3 * i, 0, 0.5, MAINSCREEN_BOUNDS.size.height);
lineView1.backgroundColor = [UIColor whiteColor];
[self addSubview:lineView1];
UIView *lineView2 = [[UIView alloc]init];
lineView2.frame = CGRectMake(0, MAINSCREEN_BOUNDS.size.height/3 * i, MAINSCREEN_BOUNDS.size.width, 0.5);
lineView2.backgroundColor = [UIColor whiteColor];
[self addSubview:lineView2];
}
}
- (void)configureUI {
[self setLineView];
[self addSubview:self.pictureButton];
[self addSubview:self.backButton];
[self addSubview:self.flashButton];
[self addSubview:self.takePhotoButton];
[self addSubview:self.scriptLabel];
[self addSubview:self.focusView];
}
#pragma mark - Action
- (void)startCamera {
if (self.captureSession) {
[self.captureSession startRunning];
}
}
- (void)stopCamera {
if (self.captureSession) {
[self.captureSession stopRunning];
}
}
// 闪光灯
- (void)flashAction:(UIButton *)sender{
if ([_delegate respondsToSelector:@selector(torchLightAction:handle:)]) {
[_delegate torchLightAction:self handle:^(NSError *error) {
if (error) {
// [self showError:error];
} else {
self.flashButton.selected = !self.flashButton.selected;
}
}];
}
}
// 相册
- (void)pictureAction:(UIButton *)sender{
if ([_delegate respondsToSelector:@selector(albumAction:)]) {
[_delegate albumAction:self];
}
}
// 拍照
- (void)takePhotoAction:(UIButton *)sender {
AVCaptureConnection *connection = [_imageOutput connectionWithMediaType:AVMediaTypeVideo];
if (connection.isVideoOrientationSupported) {
// connection.videoOrientation = [self orientationForConnection];
}
[_imageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:^(CMSampleBufferRef _Nullable imageDataSampleBuffer, NSError * _Nullable error) {
if (error) {
return;
}
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [[UIImage alloc] initWithData:imageData];
if ([self.delegate respondsToSelector:@selector(takePhotoAction:withImage:)]) {
[self.delegate takePhotoAction:self withImage:image];
}
}];
}
// 返回
- (void)backAction:(UIButton *)sender {
if ([_delegate respondsToSelector:@selector(cancelAction:)]) {
[_delegate cancelAction:self];
}
}
#pragma mark - 聚焦
//手势点击
- (void)addGenstureRecognizer {
UITapGestureRecognizer *tapGesture= [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapScreen:)];
[self addGestureRecognizer:tapGesture];
}
- (void)tapScreen:(UITapGestureRecognizer*)gesture {
CGPoint point = [gesture locationInView:gesture.view];
[self focusAtPoint:point];
}
- (void)focusAtPoint:(CGPoint)point {
CGSize size = self.bounds.size;
CGPoint focusPoint = CGPointMake( point.y /size.height ,1-point.x/size.width );
NSError *error;
if ([self.captureDevice lockForConfiguration:&error]) {
if ([self.captureDevice isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
[self.captureDevice setFocusPointOfInterest:focusPoint];
[self.captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
}
[self.captureDevice unlockForConfiguration];
}
[self setFocusCursorWithPoint:point];
}
-(void)setFocusCursorWithPoint:(CGPoint)point{
//下面是手触碰屏幕后对焦的效果
self.focusView.center = point;
self.focusView.hidden = NO;
[UIView animateWithDuration:0.3 animations:^{
self.focusView.transform = CGAffineTransformMakeScale(1.25, 1.25);
}completion:^(BOOL finished) {
[UIView animateWithDuration:0.5 animations:^{
self.focusView.transform = CGAffineTransformIdentity;
self.focusView.alpha = 0.1;
} completion:^(BOOL finished) {
self.focusView.hidden = YES;
self.focusView.alpha = 1;
}];
}];
}
- (AVCaptureVideoOrientation)orientationForConnection {
AVCaptureVideoOrientation videoOrientation = AVCaptureVideoOrientationPortrait;
switch ([UIDevice currentDevice].orientation) {
case UIDeviceOrientationLandscapeLeft:
// yes to the right, this is not bug!
videoOrientation = AVCaptureVideoOrientationLandscapeRight;
break;
case UIDeviceOrientationLandscapeRight:
videoOrientation = AVCaptureVideoOrientationLandscapeLeft;
break;
case UIDeviceOrientationPortraitUpsideDown:
videoOrientation = AVCaptureVideoOrientationPortraitUpsideDown;
break;
default:
videoOrientation = AVCaptureVideoOrientationPortrait;
break;
}
return videoOrientation;
}
#pragma mark - Lazy init
//相册按钮
- (UIButton *)pictureButton {
if (!_pictureButton) {
_pictureButton = [UIButton buttonWithType:UIButtonTypeCustom];
_pictureButton.frame = CGRectMake(self.frame.size.width - 56, kScreenHeight - 66, 40, 40);
[_pictureButton setBackgroundImage:[UIImage imageNamed:@"wrongbook_xiangce"] forState:UIControlStateNormal];
[_pictureButton addTarget:self action:@selector(pictureAction:) forControlEvents:UIControlEventTouchUpInside];
}
return _pictureButton;
}
//闪光灯按钮
- (UIButton *)flashButton {
if (!_flashButton) {
_flashButton = [UIButton buttonWithType:UIButtonTypeCustom];
_flashButton.frame = CGRectMake(16, 16, 40, 40);
[_flashButton setBackgroundImage:[UIImage imageNamed:@"wrongbook_dengpaoOff"] forState:UIControlStateNormal];
[_flashButton setBackgroundImage:[UIImage imageNamed:@"wrongbook_dengpaoOn"] forState:UIControlStateSelected];
[_flashButton addTarget:self action:@selector(flashAction:) forControlEvents:UIControlEventTouchUpInside];
}
return _flashButton;
}
//拍照按钮
- (UIButton *)takePhotoButton {
if (!_takePhotoButton) {
_takePhotoButton = [UIButton buttonWithType:UIButtonTypeCustom];
_takePhotoButton.frame = CGRectMake(kScreenWidth / 2 - 31, kScreenHeight - 76, 62, 62);
[_takePhotoButton setBackgroundImage:[UIImage imageNamed:@"wrongbook_paizhao"] forState:UIControlStateNormal];
[_takePhotoButton addTarget:self action:@selector(takePhotoAction:) forControlEvents:UIControlEventTouchUpInside];
}
return _takePhotoButton;
}
// 返回按钮
- (UIButton *)backButton {
if (!_backButton) {
_backButton = [UIButton buttonWithType:UIButtonTypeCustom];
_backButton.frame = CGRectMake(16, kScreenHeight - 66, 40, 40);
[_backButton setBackgroundImage:[UIImage imageNamed:@"wrongbook_guanbi"] forState:UIControlStateNormal];
[_backButton addTarget:self action:@selector(backAction:) forControlEvents:UIControlEventTouchUpInside];
}
return _backButton;
}
//描述文字
- (UILabel *)scriptLabel {
if (!_scriptLabel) {
_scriptLabel = [[UILabel alloc]init];
_scriptLabel.text = @"文字对齐参考线,点击画面可聚焦";
_scriptLabel.font = [UIFont systemFontOfSize:14];
_scriptLabel.backgroundColor = [UIColor clearColor];
_scriptLabel.textColor = [UIColor whiteColor];
_scriptLabel.frame = CGRectMake(80, kScreenHeight / 2 - 15, kScreenWidth - 160, 30);
_scriptLabel.textAlignment = NSTextAlignmentCenter;
}
return _scriptLabel;;
}
- (FocusView *)focusView {
if (!_focusView) {
_focusView = [[FocusView alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
_focusView.hidden = YES;
}
return _focusView;
}
参考资料:
https://www.jianshu.com/p/83d7ea181001
https://www.jianshu.com/p/69b31e80ba49











网友评论