一、基本思路
1、创建一个导航控制器,承载两个控制器:拍照、照片预览;
2、分别创建这两个控制器,由于视图均为静态视图,所以直接用xib创建即可;
3、引用AVFoundation框架,通过媒体与数据流的交互来获取图片;
4、各种封装
二、使用到的几个类及相关说明:
SAImagePickerController:继承导航控制器类,用他来创建一个自定义相机 SACameraCtrl:拍照控制器,即拍照主界面,取景,摄像头切换、闪光灯开关、拍照按钮都在这个界面上展示; SAPhotoViewCtrl:照片预览控制器,即拍照后的预览界面,查看照片效果、确定使用照片或重拍; SAPreview:自定义View,拍照取景的那个视图,自定义是为了能够监听处理该视图上的事件,如对焦等。
三、基本界面预览:
四、关键代码: 1、SACameraCtrl.h [Objective-C] 纯文本查看 复制代码 //
// SACameraCtrl.h
// Test
//
// Created by 余西安 on 15/4/13.
// Copyright (c) 2015年 Sian. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface SACameraCtrl : UIViewController
@end
2、SACameraCtrl.m [Objective-C] 纯文本查看 复制代码 //
// 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 () <UIAccelerometerDelegate>
// 输入流预览图层
@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
3、SAImagePickerController.h [Objective-C] 纯文本查看 复制代码 //
// SAImagePickerController.h
// Test
//
// Created by 余西安 on 15/4/15.
// Copyright (c) 2015年 Sian. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef void (^SAImagePickerComplete)(UIImage *image);
@interface SAImagePickerController : UINavigationController
@property (nonatomic, copy) SAImagePickerComplete completeBlock;
+ (instancetype)pickerComplete:(SAImagePickerComplete)complete;
@end
4、SAImagePickerController.m [Objective-C] 纯文本查看 复制代码 //
// 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
5、SAPhotoViewCtrl.h [Objective-C] 纯文本查看 复制代码 //
// SAPhotoViewCtrl.h
// Test
//
// Created by 余西安 on 15/4/14.
// Copyright (c) 2015年 Sian. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface SAPhotoViewCtrl : UIViewController
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
- (instancetype)initWithImage:(UIImage *)image;
@end
6、SAPhotoViewCtrl.m [Objective-C] 纯文本查看 复制代码 //
// 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
7、SAPreview.h [Objective-C] 纯文本查看 复制代码 //
// SAPreview.h
// Test
//
// Created by 余西安 on 15/4/14.
// Copyright (c) 2015年 Sian. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface SAPreview : UIView
@property (nonatomic, strong) UIImageView *aperture;
- (void)showAperture;
@end
8、SAPreview.m [Objective-C] 纯文本查看 复制代码 //
// 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
五、基本使用 一行代码搞定! [Objective-C] 纯文本查看 复制代码 SAImagePickerController *imagePicker = [SAImagePickerController pickerComplete:^(UIImage *image) {
self.imageView.image = image;
}];
[self presentViewController:imagePicker animated:YES completion:nil];
六、Demo下载
|