iOS学习笔记

AVFoundation拍照,合成gif动图。。。

2017-01-17  本文已影响167人  ChooseYourself

工作之余简单整理了下,关于使用系统框架 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还在完善中。

上一篇 下一篇

猜你喜欢

热点阅读