iOS项目实战之自定义相机

一、基本思路
1、创建一个导航控制器,承载两个控制器:拍照、照片预览;
2、分别创建这两个控制器,由于视图均为静态视图,所以直接用xib创建即可;
3、引用AVFoundation框架,通过媒体与数据流的交互来获取图片;
4、各种封装

二、使用到的几个类及相关说明:
SAImagePickerController:继承导航控制器类,用他来创建一个自定义相机
SACameraCtrl:拍照控制器,即拍照主界面,取景,摄像头切换、闪光灯开关、拍照按钮都在这个界面上展示;
SAPhotoViewCtrl:照片预览控制器,即拍照后的预览界面,查看照片效果、确定使用照片或重拍;
SAPreview:自定义View,拍照取景的那个视图,自定义是为了能够监听处理该视图上的事件,如对焦等。

三、基本界面预览:

QQ20150421-1@2xQQ20150421-2@2x

四、关键代码:
SACameraCtrl.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
//
//? SACameraCtrl.m
//? Test
//
//? Created by 余西安 on 15/4/13.
//? Copyright (c) 2015年 Sian. All rights reserved.
//
?
#import "SACameraCtrl.h"
#import "SAPreview.h"
#import "SAPhotoViewCtrl.h"
#import <AVFoundation/AVFoundation.h>
#import <CoreMotion/CoreMotion.h>
#define SourcePath [[NSBundle mainBundle] pathForResource:@"SAImagePickerController.bundle" ofType:nil]
?
@interface SACameraCtrl () 
?
// 输入流预览图层
@property (nonatomic, strong)?? AVCaptureVideoPreviewLayer? *previewLayer;
?
@property (weak, nonatomic) IBOutlet SAPreview *preview;
?
// AVCaptureSession对象来执行输入设备和输出设备之间的数据传递
@property (nonatomic, strong)?? AVCaptureSession??????????? *session;
?
// 设备输入流
@property (nonatomic, strong)?? AVCaptureDeviceInput??????? *deviceInput;
?
// 照片输出流
@property (nonatomic, strong)?? AVCaptureStillImageOutput?? *imageOutput;
?
// 图片输出视图
@property (nonatomic, strong)?? UIImageView???? *cameraShowView;
?
// 摄像头数组
@property (nonatomic, strong, readonly) NSArray *devices;?????? // AVCaptureDevices
?
// 闪光灯切换按钮
@property (weak, nonatomic) IBOutlet UIButton *flashButton;
?
?
@end
?
@implementation SACameraCtrl
?
- (void)viewDidLoad
{
????[super viewDidLoad];
????// 创建交互流
????self.session = [[AVCaptureSession alloc] init];
????self.session.sessionPreset = AVCaptureSessionPresetPhoto;
?????
????// 媒体输入
????AVCaptureDevice *device = [self.devices firstObject];
????[self setDevice:device FlashMode:AVCaptureFlashModeAuto];
????self.deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:device error:NULL];
????if([self.session canAddInput:self.deviceInput])[self.session addInput:self.deviceInput];
?????
????// 媒体输出
????self.imageOutput = [[AVCaptureStillImageOutput alloc] init];
????self.imageOutput.outputSettings = @{AVVideoCodecKey : AVVideoCodecJPEG};
????AVCaptureConnection * connection = [self.imageOutput connectionWithMediaType:AVMediaTypeVideo];
????connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight;
????if([self.session canAddOutput:self.imageOutput])[self.session addOutput:self.imageOutput];
?????
????// 媒体预览
????self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
????self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
????self.previewLayer.position = self.preview.center;
????[self.preview.layer addSublayer:self.previewLayer];
????[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
????[self setPreviewGesture];
}
?
?
#pragma mark - 前后摄像头
- (NSArray *)devices
{
????return [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
}
?
- (void)viewWillAppear:(BOOL)animated
{
????[super viewWillAppear:animated];
????self.previewLayer.frame = self.preview.layer.bounds;
????self.navigationController.navigationBar.hidden = YES;
????[self.session startRunning];
}
?
- (void)viewDidAppear:(BOOL)animated
{
????[super viewDidAppear:animated];
????[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
?
- (void)viewWillDisappear:(BOOL)animated
{
????[super viewWillDisappear:animated];
????[[UIApplication sharedApplication] setStatusBarHidden:NO];
}
?
- (void)viewDidDisappear:(BOOL)animated
{
????[super viewDidDisappear:animated];
????[self.session stopRunning];
}
?
/// 媒体预览图层手势事件
- (void)setPreviewGesture
{
????// 注册一个手势事件来实现对焦功能
????UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapPreview:)];
????[self.preview addGestureRecognizer:tap];
}
?
/// 预览图层点击事件
- (void)tapPreview:(UITapGestureRecognizer *)gesture
{
????CGPoint point = [gesture locationInView:gesture.view];
????CGPoint p = [self.previewLayer captureDevicePointOfInterestForPoint:point];
????[self focusAtPoint:p];
????self.preview.aperture.center = point;
????[self.preview showAperture];
}
?
/// 对焦事件
- (void)focusAtPoint:(CGPoint)point
{
????AVCaptureDevice *device = self.deviceInput.device;
????NSError *error;
????if ([device isFocusModeSupported:AVCaptureFocusModeAutoFocus] && [device isFocusPointOfInterestSupported])
????{
????????if ([device lockForConfiguration:&error]) {
????????????[device setFocusPointOfInterest:point];
????????????[device setFocusMode:AVCaptureFocusModeAutoFocus];
????????????[device unlockForConfiguration];
????????} else {
????????????SALog(@"Error: %@", error);
????????}
????}
}
?
/// 摄像头切换事件
- (IBAction)switchCamera:(UIButton *)sender
{
????if (self.devices.count < 2) return;
?????
????NSInteger oldIndex = [self.devices indexOfObject:self.deviceInput.device];
????NSInteger newIndex = (oldIndex + 1) % self.devices.count;
????AVCaptureDevice *newDevice = [self.devices objectAtIndex:newIndex];
????AVCaptureDeviceInput *newInput = [AVCaptureDeviceInput deviceInputWithDevice:newDevice error:NULL];
????if (newInput) {
????????[self.session beginConfiguration];
????????[self.session removeInput:self.deviceInput];
????????[self.session addInput:newInput];
????????[self.session commitConfiguration];
????????self.deviceInput = newInput;
????????self.flashButton.hidden = !newDevice.isFlashAvailable;
????}
}
?
/// 取消事件
- (IBAction)cancel:(UIButton *)sender
{
????[self dismissViewControllerAnimated:YES completion:nil];
}
?
/// 拍照事件
- (IBAction)shutter:(UIButton *)sender
{
????sender.userInteractionEnabled = NO;
????AVCaptureConnection * connection = [self.imageOutput connectionWithMediaType:AVMediaTypeVideo];
????connection.videoOrientation = AVCaptureVideoOrientationPortrait;
?????
????connection.videoScaleAndCropFactor = 1.0;?? // 获取流比例
????if (!connection) {
????????NSLog(@"take photo failed!");
????????return;
????}
?????
????[self.imageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
????????if (imageDataSampleBuffer == NULL) {
????????????return;
????????}
????????NSData * imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
????????UIImage * image = [UIImage imageWithData:imageData];
????????SAPhotoViewCtrl *photoView = [[SAPhotoViewCtrl alloc] initWithImage:image];
????????[self.navigationController pushViewController:photoView animated:NO];
????????sender.userInteractionEnabled = YES;
????}];
}
?
/// 闪光灯开关按钮
- (IBAction)overTurn:(UIButton *)sender
{
????sender.tag = (sender.tag + 1) % 3;
????NSString *imageName = [NSString stringWithFormat:@"camera_flashlight_%d.png", (int)sender.tag];
????[sender setBackgroundImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
????[self setDevice:self.deviceInput.device FlashMode:(2 - sender.tag)];
}
?
/// 切换闪光灯模式
- (void)setDevice:(AVCaptureDevice *)device FlashMode:(AVCaptureFlashMode)flashModel
{
????if (device.isFlashAvailable == NO) return;
????if ([device lockForConfiguration:nil]) {
????????device.flashMode = flashModel;
????}
????[device unlockForConfiguration];
}
?
@end

SAImagePickerController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//
//? SAImagePickerController.m
//? Test
//
//? Created by 余西安 on 15/4/15.
//? Copyright (c) 2015年 Sian. All rights reserved.
//
?
#import "SAImagePickerController.h"
#import "SACameraCtrl.h"
?
@interface SAImagePickerController ()
?
@end
?
@implementation SAImagePickerController
?
+ (instancetype)pickerComplete:(SAImagePickerComplete)complete
{
????return [[self alloc] initWithBlock:complete];
}
?
- (instancetype)initWithBlock:(SAImagePickerComplete)block
{
????if (self = [super init]){
????????if (block) self.completeBlock = block;
????}
????return self;
}
?
- (instancetype)init
{
????SACameraCtrl *cameraCtrl = [[SACameraCtrl alloc] init];
????if (self = [super initWithRootViewController:cameraCtrl]){
?????
????}
????return self;
}
?
- (void)viewDidLoad
{
????[super viewDidLoad];
}
?
- (void)dealloc
{
????SALog(@"SAImagePickerController is dealloc");
}
@end

SAPhotoViewCtrl.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//
//? SAPhotoViewCtrl.m
//? Test
//
//? Created by 余西安 on 15/4/14.
//? Copyright (c) 2015年 Sian. All rights reserved.
//
?
#import "SAPhotoViewCtrl.h"
#import "SAImagePickerController.h"
?
@interface SAPhotoViewCtrl ()
?
@property (nonatomic, strong) UIImage *image;
?
@property (nonatomic, assign) NSInteger rotate;
?
@end
?
@implementation SAPhotoViewCtrl
?
- (instancetype)initWithImage:(UIImage *)image
{
????if (self = [super initWithNibName:@"SAPhotoViewCtrl" bundle:nil]){
????????self.image = image;
????????self.rotate = 0;
????}
????return self;
}
?
- (void)viewWillAppear:(BOOL)animated
{
????[super viewWillAppear:animated];
????self.imageView.image = self.image;
????[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
?
- (void)viewWillDisappear:(BOOL)animated
{
????[super viewWillDisappear:animated];
????[[UIApplication sharedApplication] setStatusBarHidden:NO];
}
?
- (void)viewDidLoad
{
????[super viewDidLoad];
}
?
/// 旋转图片
- (IBAction)transForm:(UIButton *)sender
{
????self.rotate ++;
????[UIView animateWithDuration:0.3 animations:^{
????????self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, - M_PI_2);
????}];
}
?
/// 重新拍摄
- (IBAction)reShutter:(UIButton *)sender
{
????[self.navigationController popViewControllerAnimated:NO];
}
?
/// 使用照片
- (IBAction)submitImage:(UIButton *)sender
{
????UIImageOrientation orientation = UIImageOrientationUp;
????switch (self.rotate % 4) {
????????case 0:{
????????????orientation = UIImageOrientationRight;
????????}break;
????????case 1:{
????????????orientation = UIImageOrientationUp;
????????}break;
????????case 2:{
????????????orientation = UIImageOrientationLeft;
????????}break;
????????case 3:{
????????????orientation = UIImageOrientationDown;
????????}break;
?????????????
????????default:break;
????}
????CGFloat scale = [UIScreen mainScreen].scale;
????UIImage *image = [[UIImage alloc] initWithCGImage:self.image.CGImage scale:scale orientation:orientation];
????SAImagePickerController *pickerCtrl = (SAImagePickerController *)self.navigationController;
????pickerCtrl.completeBlock(image);
????[self dismissViewControllerAnimated:YES completion:^{
????????[self.navigationController popViewControllerAnimated:NO];
????}];
}
?
@end

SAPreview.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//
//? SAPreview.m
//? Test
//
//? Created by 余西安 on 15/4/14.
//? Copyright (c) 2015年 Sian. All rights reserved.
//
?
#import "SAPreview.h"
?
@implementation SAPreview
?
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
????if (self = [super initWithCoder:aDecoder]){
????????UIImage *image = [UIImage imageNamed:@"camera_aperture.png"];
????????self.aperture = [[UIImageView alloc] initWithImage:image];
????}
????return self;
}
?
- (void)showAperture
{
????[self addSubview:self.aperture];
????self.aperture.transform = CGAffineTransformMakeScale(2.0, 2.0);
????[UIView animateWithDuration:0.3 animations:^{
????????self.aperture.transform = CGAffineTransformIdentity;
????}];
????[self.aperture performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:1.0];
}
?
@end

五、基本使用

一行代码搞定!

1
2
3
4
SAImagePickerController *imagePicker = [SAImagePickerController? pickerComplete:^(UIImage *image) {
????self.imageView.image = image;
}];
[self presentViewController:imagePicker animated:YES completion:nil];

六、Demo下载

链接: http://pan.baidu.com/s/1bnCUte3 密码: 6k4k

One thought on “iOS项目实战之自定义相机

  1. Pingback: 小龙虾博客 (Crayfish)

Leave a Reply