工作之余简单整理了下,关于使用系统框架 AVFoudation自定义拍照功能的一些代码,还有依赖MobileCoreServices框架制作gif的一些知识,话不多说,直接上代码,不对的地方,欢迎指正。
1.PhotoCameraViewController.h
#import <UIKit/UIKit.h>
@interface PhotoCameraViewController : UIViewController
/* * 图片回调 **/
@property (nonatomic, copy) void(^CameraBlock)(NSURL *fileURL);
/* * photoCount大于1可生成gif,最大为5,默认为1 **/
@property (nonatomic, assign) NSInteger photoCount;
@end
2.PhotoCameraViewController.m 相关框架的导入和属性的声明
#import "PhotoCameraViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <ImageIO/ImageIO.h>
#import <MobileCoreServices/MobileCoreServices.h>
#define SOUNDID 1108
typedef void(^PropertyChangeBlock)(AVCaptureDevice *captureDevice);
@interface PhotoCameraViewController ()
{
NSTimer *cameraTimer;
}
@property (nonatomic, strong) AVCaptureSession *captureSession;//负责输入和输出设备之间的数据传递
@property (nonatomic, strong) AVCaptureDeviceInput *captureDeviceInput;//负责从AVCaptureDevice获得输入数据
@property (nonatomic, strong) AVCaptureStillImageOutput *captureStillImageOutput;//照片输出流 @property (nonatomic, strong) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;//相机拍摄预览图层
@property (nonatomic, strong) UIView *viewContainer; //摄像头预览的背景视图
@property (nonatomic, strong) UIView *flashView; //闪光灯选择视图
@property (nonatomic, strong) UIImageView *focusCursor; //聚焦视图
@property (nonatomic, strong) UIImageView *previewImage; //预览视图
@property (nonatomic, strong) UIButton *backButton; //返回按钮
@property (nonatomic, strong) UIButton *toggleButton; //切换前后摄像头
@property (nonatomic, strong) UIButton *flashButton; //切换闪光灯模式
@property (nonatomic, strong) UIButton *takeButton; // 拍照按钮
@property (nonatomic, strong) UIButton *resetButton; //重拍
@property (nonatomic, strong) UIButton *sureButton; //使用合成后的图片
@property (nonatomic, strong) NSMutableArray *imageArray; //存放图片的数组
@property (nonatomic, strong) NSURL *gifUrl; // 合成的图片的地址
@end
3.初始化视图界面, 这里用的代码布局,因为是自己写的小demo,没有导入第三方框架,也没有适配,求勿喷, 以4.7寸屏为示例。
#pragma mark - layoutView
- (void)initSubViews {
self.backButton = [self creatButtonWithFrame:CGRectMake(0, 20, 40, 40) title:nil imageName:@"Back" action:@selector(handleBacK:)];
self.toggleButton = [self creatButtonWithFrame:CGRectMake(kScreenWidth / 2 - 20, 20, 40, 40) title:nil imageName:@"CameraToggle" action:@selector(toggleBtnClick:)];
self.flashButton = [self creatButtonWithFrame:CGRectMake(kScreenWidth - 40, 20, 40, 40) title:nil imageName:@"CameraFlashOn" action:@selector(flashClick:)];
self.flashView = [[UIView alloc] initWithFrame:CGRectMake(CGRectGetMinX(self.flashButton.frame), CGRectGetMaxY(self.flashButton.frame), 40, 120)];
[self.flashView addSubview:[self creatButtonWithFrame:CGRectMake(0, 0, 40, 40) title:nil imageName:@"CameraFlashOn" action:@selector(flashOnClick:)]];
[self.flashView addSubview:[self creatButtonWithFrame:CGRectMake(0, 40, 40, 40) title:nil imageName:@"CameraFlashOff" action:@selector(flashOffClick:)]];
[self.flashView addSubview:[self creatButtonWithFrame:CGRectMake(0, 80, 40, 40) title:nil imageName:@"CameraFlashAuto" action:@selector(flashAutoClick:)]];
self.flashView.hidden = YES;
self.takeButton = [self creatButtonWithFrame:CGRectMake(kScreenWidth / 2 - 30, kScreenheight - 110, 60, 110) title:nil imageName:@"CameraTake" action:@selector(takeBtnClick:)];
self.resetButton = [self creatButtonWithFrame:CGRectMake(20, kScreenheight - 45, 60, 25) title:@"Retake" imageName:nil action:@selector(resetBtnClick:)];
self.resetButton.hidden = YES;
self.sureButton = [self creatButtonWithFrame:CGRectMake(kScreenWidth - 80, kScreenheight - 45, 60, 25) title:@"Use gif" imageName:nil action:@selector(useBtnClick:)];
self.sureButton.hidden = YES;
self.viewContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 64, kScreenWidth, kScreenheight - 174)];
[self.viewContainer addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]];
}
#pragma mark - Creat button
- (UIButton *)creatButtonWithFrame:(CGRect)frame title:(NSString *)title imageName:(NSString *)imageName action:(SEL)action {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = frame;
if (imageName) [button setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];
if (title) [button setTitle:title forState:UIControlStateNormal];
[button addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
return button;
}
4.初始化摄像头的一些相关配置
#pragma mark - captureSession
- (void)configureSession {
_captureSession = [[AVCaptureSession alloc] init];
if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720])
{
[_captureSession setSessionPreset:AVCaptureSessionPreset1280x720];
}
AVCaptureDevice *captureDevice = [self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];
if (!captureDevice) {
NSLog(@"取得后置摄像头时出现问题.");
return;
}
NSError *error = nil;
_captureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:captureDevice error:&error];
if (error) {
NSLog(@"取得设备输入对象时出错,错误原因:%@",error.localizedDescription);
return;
}
_captureStillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSetting = @{AVVideoCodecKey : AVVideoCodecJPEG};
[_captureStillImageOutput setOutputSettings:outputSetting];
if ([_captureSession canAddInput:_captureDeviceInput])
{
[_captureSession addInput:_captureDeviceInput];
}
if ([_captureSession canAddOutput:_captureStillImageOutput])
{
[_captureSession addOutput:_captureStillImageOutput];
}
_captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
_captureVideoPreviewLayer.frame = self.viewContainer.layer.bounds;
_captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.viewContainer.layer insertSublayer:_captureVideoPreviewLayer atIndex:0];
[self.captureSession commitConfiguration];
[self.captureSession startRunning];
}
5.设置聚焦视图和聚焦,曝光,闪光灯模式。。
#pragma mark 聚焦框
- (void)setFocusCursorWithPoint:(CGPoint)point
{
_focusCursor.center = point;
_focusCursor.transform = CGAffineTransformMakeScale(1.5, 1.5);
_focusCursor.alpha = 1.0;
[UIView animateWithDuration:1.0 animations:^{
self.focusCursor.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
self.focusCursor.alpha = 0;
}];
}
#pragma mark 设置聚焦点
- (void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point
{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isFocusModeSupported:focusMode])
{
[captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
}
if ([captureDevice isFocusPointOfInterestSupported])
{
[captureDevice setFocusPointOfInterest:point];
}
if ([captureDevice isExposureModeSupported:exposureMode])
{
[captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];
}
if ([captureDevice isExposurePointOfInterestSupported])
{
[captureDevice setExposurePointOfInterest:point];
}
}];
}
#pragma mark 设置聚焦模式
- (void)setFocusMode:(AVCaptureFocusMode)focusMode
{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isFocusModeSupported:focusMode])
{
[captureDevice setFocusMode:focusMode];
}
}];
}
#pragma mark 设置曝光模式
- (void)setExposureMode:(AVCaptureExposureMode)exposureMode
{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isExposureModeSupported:exposureMode])
{
[captureDevice setExposureMode:exposureMode];
}
}];
}
#pragma mark 设置闪光灯模式
- (void)setFlashMode:(AVCaptureFlashMode)flashMode
{
switch (flashMode) {
case AVCaptureFlashModeOff:
[_flashButton setImage:[UIImage imageNamed:@"CameraFlashOff"] forState:UIControlStateNormal];
break;
case AVCaptureFlashModeOn:
[_flashButton setImage:[UIImage imageNamed:@"CameraFlashOn"] forState:UIControlStateNormal];
break;
case AVCaptureFlashModeAuto:
[_flashButton setImage:[UIImage imageNamed:@"CameraFlashAuto"] forState:UIControlStateNormal];
break;
default:
break;
}
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isFlashModeSupported:flashMode])
{
[captureDevice setFlashMode:flashMode];
}
}];
}
- (void)changeDeviceProperty:(PropertyChangeBlock)propertyChange
{
AVCaptureDevice *captureDevice = [_captureDeviceInput device];
NSError *error = nil;
// 注意改变设备属性前先加锁,调用完解锁
if ([captureDevice lockForConfiguration:&error])
{
propertyChange(captureDevice);
[captureDevice unlockForConfiguration];
}
else NSLog(@"changeDevicePropertyError:%@",error.localizedDescription);
}
6.切换摄像头,聚焦视图的显示。。
//切换摄像头
- (void)toggleBtnClick:(UIButton *)sender {
AVCaptureDevice *currentDevice = [_captureDeviceInput device];
AVCaptureDevicePosition currentPosition = [currentDevice position];
AVCaptureDevice *toChangeDevice;
AVCaptureDevicePosition toChangePosition = AVCaptureDevicePositionFront;
if (currentPosition == AVCaptureDevicePositionUnspecified || currentPosition == AVCaptureDevicePositionFront)
{
toChangePosition = AVCaptureDevicePositionBack;
}
toChangeDevice = [self getCameraDeviceWithPosition:toChangePosition];
// 获得要调整的设备输入对象
AVCaptureDeviceInput *toChangeDeviceInput = [[AVCaptureDeviceInput alloc]initWithDevice:toChangeDevice error:nil];
// 改变会话的配置前一定要先开启配置,配置完成后提交配置改变
[self.captureSession beginConfiguration];
// 移除原有输入对象
[self.captureSession removeInput:self.captureDeviceInput];
// 添加新的输入对象
if ([self.captureSession canAddInput:toChangeDeviceInput])
{
[self.captureSession addInput:toChangeDeviceInput];
self.captureDeviceInput = toChangeDeviceInput;
}
// 提交会话配置
[self.captureSession commitConfiguration];
[self setFlashMode:AVCaptureFlashModeAuto];
}
#pragma mark - Tap Action
- (void)handleTap:(UITapGestureRecognizer *)gesture {
CGPoint point = [gesture locationInView:self.viewContainer];
// 将UI坐标转化为摄像头坐标
CGPoint cameraPoint= [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];
[self setFocusCursorWithPoint:point];
[self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint];
}
7.拍照,生成gif。。
//拍照
- (void)takeBtnClick:(UIButton *)sender {
cameraTimer = [NSTimer scheduledTimerWithTimeInterval:1.2 target:self selector:@selector(takePhotos) userInfo:nil repeats:YES];
}
- (void)takePhotos
{
__weak PhotoCameraViewController *weakSelf = self;
AudioServicesPlaySystemSound(SOUNDID);
// 根据设备输出获得连接
AVCaptureConnection *captureConnection = [_captureStillImageOutput connectionWithMediaType:AVMediaTypeVideo];
// 根据连接取得设备输出的数据
[_captureStillImageOutput captureStillImageAsynchronouslyFromConnection:captureConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error)
{
if (imageDataSampleBuffer)
{
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
UIImage *image = [UIImage imageWithData:imageData];
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
UIImage *lastImg = [UIImage squareImage:image scaledToSize:kScreenWidth];
_previewImage.image = image;
_previewImage.hidden = NO;
[weakSelf.imageArray addObject:lastImg];
if (_imageArray.count == _photoCount) {
[cameraTimer invalidate];
[self takePhotoFinished];
} else {
_takeButton.hidden = YES;
_backButton.hidden = YES;
}
}
}];
}
#pragma mark 完成拍摄条件
- (void)takePhotoFinished
{
_backButton.hidden = YES;
_toggleButton.hidden = YES;
_takeButton.hidden = YES;
_resetButton.hidden = NO;
_sureButton.hidden = NO;
_flashButton.hidden = YES;
_previewImage.hidden = NO;
[self makeGif];
}
#pragma mark 生成gif
- (void)makeGif
{
NSDictionary *fileProperties = @{(__bridge id)kCGImagePropertyGIFDictionary:@{(__bridge id)kCGImagePropertyGIFLoopCount:@0,}};
NSDictionary *frameProperties = @{(__bridge id)kCGImagePropertyGIFDictionary:@{(__bridge id)kCGImagePropertyGIFDelayTime:@0.5f,}};
NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil];
NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:@"animated.gif"];
CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)fileURL, kUTTypeGIF, self.imageArray.count, NULL);
CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)fileProperties);
for (NSUInteger i = 0; i < self.imageArray.count; i++)
{
@autoreleasepool
{
CGImageDestinationAddImage(destination, ((UIImage *)self.imageArray[i]).CGImage, (__bridge CFDictionaryRef)frameProperties);
}
}
if (!CGImageDestinationFinalize(destination))
{
NSLog(@"failed to finalize image destination");
}
CFRelease(destination);
self.gifUrl = fileURL;
}
8.其他的一些按钮事件和方法。。。
//重拍
- (void)resetBtnClick:(UIButton *)sender {
if (_imageArray)
{
[_imageArray removeAllObjects];
}
_backButton.hidden = NO;
_toggleButton.hidden = NO;
_takeButton.hidden = NO;
_resetButton.hidden = YES;
_sureButton.hidden = YES;
_flashView.hidden = YES;
_previewImage.hidden = YES;
}
//确认
- (void)useBtnClick:(UIButton *)sender {
if(self.CameraBlock)
{
self.CameraBlock(self.gifUrl);
}
[self handleBacK:_backButton];
}
//打开闪光灯
- (void)flashOnClick:(UIButton *)sender {
[self setFlashMode:AVCaptureFlashModeOn];
_flashView.hidden = YES;
}
//关闭
- (void)flashOffClick:(UIButton *)sender {
[self setFlashMode:AVCaptureFlashModeOff];
_flashView.hidden = YES;
}
//自动
- (void)flashAutoClick:(UIButton *)sender {
[self setFlashMode:AVCaptureFlashModeAuto];
_flashView.hidden = YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark -
- (AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition)position
{
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *camera in devices)
{
if ([camera position] == position)
{
return camera;
}
}
return nil;
}
//闪关灯模式
- (void)flashClick:(UIButton *)sender {
_flashView.hidden = NO;
}
//返回
- (void)handleBacK:(UIButton *)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
//初始化
-(instancetype)init {
self = [super init];
if (self) {
self.photoCount = 1;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
[self initSubViews];
[self configureSession];
// Do any additional setup after loading the view.
}
#pragma mark - 懒加载
- (NSMutableArray *)imageArray {
if (!_imageArray) {
_imageArray = [NSMutableArray array];
}
return _imageArray;
}
- (UIImageView *)previewImage {
if (!_previewImage) {
_previewImage = [[UIImageView alloc] initWithFrame:self.view.bounds];
_previewImage.hidden = YES;
}
return _previewImage;
}
9.因为我是单独写的demo,这个类是单独写的,是从根视图view controller push过来的。下面附上view controller 的代码,这里用到了SDWebImage来加载gif图片。
#import "ViewController.h"
#import "PhotoCameraViewController.h"
#import "UIImage+GIF.h"
@interface ViewController ()
@property (nonatomic, strong) UIImageView *photoView;
@property (nonatomic, strong) UIButton *presentButton;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubview:self.photoView];
[self.view addSubview:self.presentButton];
// Do any additional setup after loading the view, typically from a nib.
}
- (UIImageView *)photoView {
if (!_photoView) {
_photoView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 120, 120)];
_photoView.layer.borderColor = [UIColor orangeColor].CGColor;
_photoView.layer.borderWidth = 2.f;
_photoView.layer.cornerRadius = 60;
_photoView.layer.masksToBounds = YES;
_photoView.center = CGPointMake(kScreenWidth / 2, 130);
}
return _photoView;
}
- (UIButton *)presentButton {
if (!_presentButton) {
_presentButton = [UIButton buttonWithType:UIButtonTypeCustom];
_presentButton.frame = CGRectMake(0, 0, 120, 40);
_presentButton.center = CGPointMake(kScreenWidth / 2, kScreenheight / 2);
_presentButton.layer.cornerRadius = 20;
_presentButton.layer.masksToBounds = YES;
_presentButton.backgroundColor = [UIColor colorWithRed:24 / 255.0 green:180/255.0 blue:237 / 255.0 alpha:1.0];
[_presentButton addTarget:self action:@selector(handleAction:) forControlEvents:UIControlEventTouchUpInside];
[_presentButton setTitle:@"点击拍照" forState:UIControlStateNormal];
}
return _presentButton;
}
- (void)handleAction:(UIButton *)sender {
PhotoCameraViewController *photoVC = [[PhotoCameraViewController alloc] init];
photoVC.photoCount = 3;
photoVC.CameraBlock = ^(NSURL *fileURL){
_photoView.image = [UIImage sd_animatedGIFWithData:[NSData dataWithContentsOfURL:fileURL]];
};
[self presentViewController:photoVC animated:YES completion:nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
先写到这里,还有很多不完善的地方,文章有写的不对地方还望指出,demo还在完善中。
网友评论