叫我龙哥的博客

叫我龙哥的博客

iOS自定义相机的实现

iOS自定义相机的实现

本文主要介绍iOS系统上的自定义相机的实现,其实并不难主要包含了,拍摄设备,输入端,输出端,以及抓取图像,保存图像的操作。

简单介绍组要控件

  • 上方功能区
    • 取消按钮
    • 闪光灯按钮
    • 相机切换按钮
  • 下方功能区
    • 拍照按钮
    • 重新拍照按钮
    • 选择照片按钮
  • 图像展示视图

主要代码展示

头文件内容展示

  • 定了一个block,用于传输照片信息
#import <UIKit/UIKit.h>
typedef void(^ImageBlock)(NSDictionary *imageDictionary);
@interface CameraViewController : UIViewController
@property (nonatomic, copy) ImageBlock imageblock;
-(void)setImageblock:(void(^)(NSDictionary *imageDictionary))imageblock;
@end

视图控件代码,逻辑代码展示

  • 当前视图需要的主要空控件,为了方便操作全部定义为属性
@interface CameraViewController ()<AVCaptureMetadataOutputObjectsDelegate,UIAlertViewDelegate,CAAnimationDelegate>
//捕获设备,通常是前置摄像头,后置摄像头,麦克风(音频输入)
@property(nonatomic)AVCaptureDevice *device;
//AVCaptureDeviceInput 代表输入设备,他使用AVCaptureDevice 来初始化
@property(nonatomic)AVCaptureDeviceInput *input;
//当启动摄像头开始捕获输入
@property(nonatomic)AVCaptureMetadataOutput *output;
//输出
@property (nonatomic)AVCaptureStillImageOutput *ImageOutPut;
//session:由他把输入输出结合在一起,并开始启动捕获设备(摄像头)
@property(nonatomic)AVCaptureSession *session;
//图像预览层,实时显示捕获的图像
@property(nonatomic)AVCaptureVideoPreviewLayer *previewLayer;
//设备
@property (nonatomic, strong)AVCaptureDevice *deveice;
//拍照
@property (nonatomic, strong) UIButton *PhotoButton;
//闪光灯
@property (nonatomic, strong) UIButton *flashButton;
//取消
@property (nonatomic, strong) UIButton *cancleButton;
//切换摄像头
@property (nonatomic, strong) UIButton *changeButton;
//确定选择当前照片
@property (nonatomic, strong) UIButton *selectButton;
//重新拍照
@property (nonatomic, strong) UIButton *reCamButton;
//照片加载视图
@property (nonatomic, strong) UIImageView *imageView;
//对焦区域
@property (nonatomic, strong) UIImageView *focusView;
//上方功能区
@property (nonatomic, strong) UIView *topView;
//下方功能区
@property (nonatomic, strong) UIView *bottomView;
//闪光灯状态
@property (nonatomic, assign) BOOL isflashOn;
//拍到的照片
@property (nonatomic, strong) UIImage *image;
//照片的信息
@property (nonatomic, strong) NSDictionary *imageDict;
//是否可以拍照
@property (nonatomic, assign) BOOL canCa;
//闪光灯模式
@property (nonatomic, assign) AVCaptureFlashMode flahMode;
//前后摄像头
@property (nonatomic, assign) AVCaptureDevicePosition cameraPosition;
//模糊视图
@property (nonatomic, strong) UIVisualEffectView *effectView;
@end
  • 使用懒加载,减少主要函数中的代码,看起来更为清爽。
@implementation CameraViewController
#pragma mark - 更改摄像头

-(UIVisualEffectView *)effectView{
    if (_effectView == nil) {
        UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
        _effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
        _effectView.frame = CGRectMake(0, 0, ScreenWidth(), ScreenHieght());
        _effectView.alpha = 1;
    }
    return _effectView;
}
 #pragma mark - 更改闪光灯状态
-(void)setIsflashOn:(BOOL)isflashOn{
    _isflashOn = isflashOn;
    [[NSUserDefaults standardUserDefaults] setObject:@(_isflashOn) forKey:@"flashMode"];
    if (_isflashOn) {
        [self.flashButton setBackgroundImage:[UIImage imageNamed:@"flash_on"] forState:UIControlStateNormal];
    }else{
        [self.flashButton setBackgroundImage:[UIImage imageNamed:@"flash_off"] forState:UIControlStateNormal];
    }
}
#pragma mark - 上方功能区
-(UIView *)topView{
    if (!_topView ) {
        _topView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth(), 50)];
        _topView.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.2];
        [_topView addSubview:self.cancleButton];
        [_topView addSubview:self.flashButton];
        [_topView addSubview:self.changeButton];
    }
    return _topView;
}
#pragma mark - 取消
-(UIButton *)cancleButton{
    if (_cancleButton == nil) {
        _cancleButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _cancleButton.frame = CGRectMake(20, 10, 60, 30);
        [_cancleButton setTitle:@"取消" forState:UIControlStateNormal];
        [_cancleButton addTarget:self action:@selector(cancle) forControlEvents:UIControlEventTouchUpInside];
    }
    return  _cancleButton ;
}
#pragma mark - 闪光灯
-(UIButton *)flashButton{
    if (_flashButton == nil) {
        _flashButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _flashButton.frame = CGRectMake((ScreenWidth()-30)/2.0, 10, 30, 30);
        [_flashButton addTarget:self action:@selector(FlashOn) forControlEvents:UIControlEventTouchUpInside];
    }
    return  _flashButton;
}
#pragma mark - 切换摄像头
-(UIButton *)changeButton{
    if (_changeButton == nil) {
        _changeButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _changeButton.frame = CGRectMake(ScreenWidth()-40, 10, 30, 30);
        [_changeButton setBackgroundImage:[UIImage imageNamed:@"cam"] forState:UIControlStateNormal];
        [_changeButton addTarget:self action:@selector(changeCamera) forControlEvents:UIControlEventTouchUpInside];
    }
    return  _changeButton;
}

#pragma mark - 下方功能区

-(UIView *)bottomView{
    if (!_bottomView) {
        _bottomView = [[UIView alloc] initWithFrame:CGRectMake(0, ScreenHieght()-80, ScreenWidth(), 80)];
        _bottomView.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.4];
        [_bottomView addSubview:self.reCamButton];
        [_bottomView addSubview:self.PhotoButton];
        [_bottomView addSubview:self.selectButton];
    }
    return _bottomView;
}
-(UIButton *)reCamButton{
    if (_reCamButton == nil) {
        _reCamButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _reCamButton.frame = CGRectMake(40, 25, 80, 30);
        [_reCamButton addTarget:self action:@selector(reCam) forControlEvents:UIControlEventTouchUpInside];
        [_reCamButton setTitle:@"重新拍照" forState:UIControlStateNormal];
        [_reCamButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        _reCamButton.alpha = 0;
    }
    return _reCamButton;
}
-(UIButton *)PhotoButton{
    if (_PhotoButton == nil) {
        _PhotoButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _PhotoButton.frame = CGRectMake(ScreenWidth()/2.0-30, 10, 60, 60);
        [_PhotoButton setImage:[UIImage imageNamed:@"photograph"] forState: UIControlStateNormal];
        [_PhotoButton setImage:[UIImage imageNamed:@"photograph_Select"] forState:UIControlStateNormal];
        [_PhotoButton addTarget:self action:@selector(shutterCamera) forControlEvents:UIControlEventTouchUpInside];
    }
    return _PhotoButton;
}
-(UIButton *)selectButton{
    if (_selectButton == nil) {
        _selectButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _selectButton.frame = CGRectMake(ScreenWidth()-120, 25, 80, 30);
        [_selectButton addTarget:self action:@selector(selectImage) forControlEvents:UIControlEventTouchUpInside];
        [_selectButton setTitle:@"选择照片" forState:UIControlStateNormal];
        [_selectButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        _selectButton.alpha = 0;
    }
    return _selectButton;
}
#pragma mark - 加载照片的视图
-(UIImageView *)imageView{
    if (_imageView == nil) {
        _imageView = [[UIImageView alloc]initWithFrame:self.previewLayer.frame];
        _imageView.layer.masksToBounds = YES;
        _imageView.image = _image;
    }
    return _imageView;
}
#pragma mark - 对焦区域
-(UIImageView *)focusView{
    if (_focusView == nil) {
        _focusView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 80, 80)];
        _focusView.backgroundColor = [UIColor clearColor];
        _focusView.image = [UIImage imageNamed:@"foucs80pt"];
    }
    return _focusView;
}
#pragma mark - 使用self.session,初始化预览层,self.session负责驱动input进行信息的采集,layer负责把图像渲染显示
-(AVCaptureVideoPreviewLayer *)previewLayer{
    if (_previewLayer == nil) {
        _previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:self.session];
        _previewLayer.frame = CGRectMake(0, 0, ScreenWidth(), ScreenHieght());
        _previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    }
    return  _previewLayer;
}
-(AVCaptureStillImageOutput *)ImageOutPut{
    if (_ImageOutPut == nil) {
        _ImageOutPut = [[AVCaptureStillImageOutput alloc] init];
    }
    return _ImageOutPut;
}
#pragma mark - 初始化输入
-(AVCaptureDeviceInput *)input{
    if (_input == nil) {

        _input = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:nil];
    }
    return _input;
}
#pragma mark - 初始化输出
-(AVCaptureMetadataOutput *)output{
    if (_output == nil) {

        _output = [[AVCaptureMetadataOutput alloc]init];
    }
    return  _output;
}
#pragma mark - 使用AVMediaTypeVideo 指明self.device代表视频,默认使用后置摄像头进行初始化
-(AVCaptureDevice *)device{
    if (_device == nil) {

        _device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    }
    return _device;
}

主要逻辑代码

#pragma mark - 当前视图控制器的初始化
- (instancetype)init
{
    self = [super init];
    if (self) {
        _canCa = [self canUserCamear];
    }
    return self;
}

-(void)setImageblock:(void (^)(NSDictionary *))imageblock{
    _imageblock = imageblock;
}
#pragma mark - 检查相机权限
- (BOOL)canUserCamear{
    AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if (authStatus == AVAuthorizationStatusDenied) {
        UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@"请打开相机权限" message:@"设置-隐私-相机" delegate:self cancelButtonTitle:@"确定" otherButtonTitles:@"取消", nil];
        alertView.tag = 100;
        [alertView show];
        return NO;
    }
    else{
        return YES;
    }
    return YES;
}
#pragma mark - 视图加载
- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor clearColor];
    if (_canCa) {
        [self customCamera];
        [self customUI];
        [self FlashOn];
    }else{
        return;
    }
    // Do any additional setup after loading the view, typically from a nib.
}
#pragma mark - 自定义视图
- (void)customUI{
    [self.view addSubview:self.topView];
    [self.view addSubview:self.bottomView];
    [self.view addSubview:self.focusView];
    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(focusGesture:)];
    [self.view addGestureRecognizer:tapGesture];

}
#pragma mark - 自定义相机
- (void)customCamera{
    //生成会话,用来结合输入输出
    self.session = [[AVCaptureSession alloc]init];
    if ([self.session canSetSessionPreset:AVCaptureSessionPresetPhoto]) {
        self.session.sessionPreset = AVCaptureSessionPresetPhoto;
    }
    if ([self.session canAddInput:self.input]) {
        [self.session addInput:self.input];
    }

    if ([self.session canAddOutput:self.ImageOutPut]) {
        [self.session addOutput:self.ImageOutPut];
    }

    [self.view.layer addSublayer:self.previewLayer];

    //开始启动
    [self.session startRunning];
    if ([self.device lockForConfiguration:nil]) {
        if ([self.device isFlashModeSupported:AVCaptureFlashModeAuto]) {
            [self.device setFlashMode:AVCaptureFlashModeAuto];
        }
        //自动白平衡
        if ([self.device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeAutoWhiteBalance]) {
            [self.device setWhiteBalanceMode:AVCaptureWhiteBalanceModeAutoWhiteBalance];
        }

        [self.device unlockForConfiguration];
    }
    [self focusAtPoint:self.view.center];
}

*闪光灯

#pragma 闪光灯
- (void)FlashOn{
    if ([self.device lockForConfiguration:nil]) {
        if (self.isflashOn) {
            if ([self.device isFlashModeSupported:AVCaptureFlashModeOff]) {
                [self.device setFlashMode:AVCaptureFlashModeOff];
                self.isflashOn = NO;
                //[self.flashButton setTitle:@"关" forState:UIControlStateNormal];
            }
        }else{
            if ([self.device isFlashModeSupported:AVCaptureFlashModeAuto]) {
                [self.device setFlashMode:AVCaptureFlashModeAuto];
                self.isflashOn = YES;
                //[self.flashButton setTitle:@"开" forState:UIControlStateNormal];
            }
        }

        [self.device unlockForConfiguration];
    }
}
  • 双摄像头切换,切换时使用高斯模糊对试图进行处理
#pragma mark - 相机切换
- (void)changeCamera{
    NSUInteger cameraCount = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count];
    if (cameraCount > 1) {
        self.changeButton.userInteractionEnabled = NO;
        [self cutoff];
        NSError *error;

        CATransition *animation = [CATransition animation];
        animation.duration = 1;
        animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        animation.type = @"oglFlip";
        animation.delegate = self;
        AVCaptureDevice *newCamera = nil;
        AVCaptureDeviceInput *newInput = nil;
        AVCaptureDevicePosition position = [[self.input device] position];
        if (position == AVCaptureDevicePositionFront){
            newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
            animation.subtype = kCATransitionFromLeft;
            self.cameraPosition = AVCaptureDevicePositionBack;
        }else {
            newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
            animation.subtype = kCATransitionFromRight;
            self.cameraPosition = AVCaptureDevicePositionFront;
        }

        newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];
        [self.previewLayer addAnimation:animation forKey:nil];
        /*
         高斯模糊
         */

        [self.imageView addSubview:self.effectView];
        [self.view insertSubview:self.imageView belowSubview:self.topView];
        //
        if (newInput != nil) {
            [self.session beginConfiguration];
            [self.session removeInput:self.input];
            if ([self.session canAddInput:newInput]) {
                [self.session addInput:newInput];
                self.input = newInput;

            } else {
                [self.session addInput:self.input];
            }
            [self.session commitConfiguration];

        } else if (error) {
            NSLog(@"toggle carema failed, error = %@", error);
        }
        //[self.session startRunning];
    }
}
- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position{
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    for ( AVCaptureDevice *device in devices )
        if ( device.position == position ) return device;
    return nil;
}
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
    self.changeButton.userInteractionEnabled = YES;
    [self.effectView removeFromSuperview];
    [self.imageView removeFromSuperview];
    if (self.cameraPosition == AVCaptureDevicePositionFront) {
        self.flashButton.alpha = 0;
    }else if (self.cameraPosition == AVCaptureDevicePositionBack){
        self.flashButton.alpha = 1;
    }
    [self.session startRunning];
}
  • 摄像头对焦
#pragma mark - 聚焦
- (void)focusGesture:(UITapGestureRecognizer*)gesture{
    CGPoint point = [gesture locationInView:gesture.view];
    [self focusAtPoint:point];
}
- (void)focusAtPoint:(CGPoint)point{
    CGSize size = self.view.bounds.size;
    CGPoint focusPoint = CGPointMake( point.y /size.height ,1-point.x/size.width );
    NSError *error;
    if ([self.device lockForConfiguration:&error]) {

        if ([self.device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
            [self.device setFocusPointOfInterest:focusPoint];
            [self.device setFocusMode:AVCaptureFocusModeAutoFocus];
        }

        if ([self.device isExposureModeSupported:AVCaptureExposureModeAutoExpose ]) {
            [self.device setExposurePointOfInterest:focusPoint];
            [self.device setExposureMode:AVCaptureExposureModeAutoExpose];
        }

        [self.device unlockForConfiguration];
        self.focusView.center = point;

        //[self startFocusAnimation];
        self.focusView.alpha = 1;
        [UIView animateWithDuration:0.2 animations:^{
            self.focusView.transform = CGAffineTransformMakeScale(1.25f, 1.25f);
        } completion:^(BOOL finished) {
            [UIView animateWithDuration:0.3 animations:^{
                self.focusView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
            } completion:^(BOOL finished) {
                [self hiddenFocusAnimation];
            }];
        }];
    }

}

主要功能按钮 拍照按钮

#pragma mark - 拍照
- (void)shutterCamera
{

    AVCaptureConnection * videoConnection = [self.ImageOutPut connectionWithMediaType:AVMediaTypeVideo];
    if (!videoConnection) {
        NSLog(@"take photo failed!");
        return;
    }

    [self.ImageOutPut captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
        if (imageDataSampleBuffer == NULL) {
            return;
        }
        NSData * imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
        self.image = [UIImage imageWithData:imageData];
        self.imageDict = @{@"image":self.image,@"info":@{@"PHImageFileUTIKey":@".jpeg"}};
        [self.session stopRunning];
        //[self.view insertSubview:self.imageView belowSubview:self.PhotoButton];
        [self.view insertSubview:self.imageView aboveSubview:self.topView];
        NSLog(@"image size = %@",NSStringFromCGSize(self.image.size));
        self.topView.alpha = 0;
        self.PhotoButton.alpha = 0;
        self.reCamButton.alpha = 1;
        self.selectButton.alpha = 1;
    }];
}
  • 保存至相册
#pragma - 保存至相册
- (void)saveImageToPhotoAlbum:(UIImage*)savedImage
{

    UIImageWriteToSavedPhotosAlbum(savedImage, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL);

}
// 指定回调方法

- (void)image: (UIImage *) image didFinishSavingWithError: (NSError *) error contextInfo: (void *) contextInfo{
    if(error != NULL){
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"保存图片结果提示"
                                                        message:@"保存图片失败"
                                                       delegate:self
                                              cancelButtonTitle:@"确定"
                                              otherButtonTitles:nil];
        [alert show];
    }
}
  • 回上层视图
#pragma mark - 取消 返回上级
-(void)cancle{
    [self.imageView removeFromSuperview];
    [self.session stopRunning];
    [self.navigationController popViewControllerAnimated:YES];
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    if (buttonIndex == 0 && alertView.tag == 100) {

        NSURL * url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];

        if([[UIApplication sharedApplication] canOpenURL:url]) {

            [[UIApplication sharedApplication] openURL:url];

        }
    }
}
  • 重新拍照
#pragma mark - 重新拍照
- (void)reCam{
    self.imageView.image = nil;
    [self.imageView removeFromSuperview];
    [self.session startRunning];
    self.topView.alpha = 1;
    self.PhotoButton.alpha = 1;
    self.reCamButton.alpha = 0;
    self.selectButton.alpha = 0;
}
  • 重新拍照
#pragma mark - 选择照片 返回上级
- (void)selectImage{
    [self saveImageToPhotoAlbum:self.image];
    self.imageblock(self.image);
    [self.navigationController popViewControllerAnimated:YES];
}
-(void)viewDidDisappear:(BOOL)animated{

}

- (void)focusDidFinsh{
    self.focusView.hidden = YES;
    self.focusView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
    //self.focusView.transform=CGAffineTransformMakeScale(0.7f, 0.7f);
}

对焦框动画

- (void)startFocusAnimation{
    self.focusView.hidden = NO;
    self.focusView.transform = CGAffineTransformMakeScale(1.25f, 1.25f);//将要显示的view按照正常比例显示出来
    [UIView beginAnimations:nil context:UIGraphicsGetCurrentContext()];
    //[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];  //InOut 表示进入和出去时都启动动画
    //[UIView setAnimationWillStartSelector:@selector(hiddenFoucsView)];
    [UIView setAnimationDidStopSelector:@selector(hiddenFocusAnimation)];
    [UIView setAnimationDuration:0.5f];//动画时间
    self.focusView.transform = CGAffineTransformIdentity;//先让要显示的view最小直至消失
    [UIView commitAnimations]; //启动动画
    //相反如果想要从小到大的显示效果,则将比例调换

}
- (void)hiddenFocusAnimation{
    [UIView beginAnimations:nil context:UIGraphicsGetCurrentContext()];
    //NSDate *DATE = [NSDate date];
    //[UIView setAnimationStartDate:[NSDate date]];
    [UIView setAnimationDelay:3];
    self.focusView.alpha = 0;
    [UIView setAnimationDuration:0.5f];//动画时间
    [UIView commitAnimations];

}
- (void)hiddenFoucsView{
    self.focusView.alpha = !self.focusView.alpha;
}

这里写图片描述
这里写图片描述

写在最后

  • 第一次自定义一个相机,代码写的可能不太好理解。
  • 这个项目在码云的git库上有,地址为https://git.oschina.net/LiynXu/PhotoDemo.git
  • 如果有任何意见或者建议,或者发现bug(应该是有的),请移步值git库下给予指导,3Q。
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ZuoWeiXiaoDuZuoZuo/article/details/53229019
文章标签: 自定义相机
个人分类: 简单的封装
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭