年年有"余"

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 12508|回复: 15

[SAFoundation] iOS项目实战之自定义相机

[复制链接]
  • TA的每日心情
    奋斗
    2022-12-13 21:26
  • 签到天数: 371 天

    [LV.9]以坛为家II

    发表于 2015-4-21 13:09:20 | 显示全部楼层 |阅读模式
    一、基本思路
    1、创建一个导航控制器,承载两个控制器:拍照、照片预览;
    2、分别创建这两个控制器,由于视图均为静态视图,所以直接用xib创建即可;
    3、引用AVFoundation框架,通过媒体与数据流的交互来获取图片;
    4、各种封装


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

    三、基本界面预览:

    QQ20150421-1@2x.png QQ20150421-2@2x.png

    四、关键代码:
    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下载
    游客,如果您要查看本帖隐藏内容请回复

  • TA的每日心情
    恶心
    2015-11-23 14:20
  • 签到天数: 92 天

    [LV.6]常住居民II

    发表于 2015-4-22 01:57:09 | 显示全部楼层
    写得非常好
  • TA的每日心情
    恶心
    2015-11-23 14:20
  • 签到天数: 92 天

    [LV.6]常住居民II

    发表于 2015-4-23 14:36:25 | 显示全部楼层
    请问一下 , 你所使用的字体是什么名字呢 ?
  • TA的每日心情
    奋斗
    2022-12-13 21:26
  • 签到天数: 371 天

    [LV.9]以坛为家II

     楼主| 发表于 2015-4-24 09:35:10 | 显示全部楼层
    喵了个咪 发表于 2015-4-23 14:36
    请问一下 , 你所使用的字体是什么名字呢 ?

    按钮字体吗?系统自带默认字体,没有修改。
  • TA的每日心情
    恶心
    2015-11-23 14:20
  • 签到天数: 92 天

    [LV.6]常住居民II

    发表于 2015-4-24 09:45:38 | 显示全部楼层
    Sian 发表于 2015-4-24 09:35
    按钮字体吗?系统自带默认字体,没有修改。

    不是按钮字体 , 是代码字体 , 就是帖子里黑色背景下的代码字体 , 请问用的是哪个字体呢 ?
  • TA的每日心情
    奋斗
    2022-12-13 21:26
  • 签到天数: 371 天

    [LV.9]以坛为家II

     楼主| 发表于 2015-4-24 09:54:14 | 显示全部楼层
    喵了个咪 发表于 2015-4-24 09:45
    不是按钮字体 , 是代码字体 , 就是帖子里黑色背景下的代码字体 , 请问用的是哪个字体呢 ?

    在功能区插入表情旁边有个插入代码,选择Objective-C语言,系统会自动布局。
  • TA的每日心情
    恶心
    2015-11-23 14:20
  • 签到天数: 92 天

    [LV.6]常住居民II

    发表于 2015-4-24 10:06:04 | 显示全部楼层
    Sian 发表于 2015-4-24 09:54
    在功能区插入表情旁边有个插入代码,选择Objective-C语言,系统会自动布局。

    那请问系统用的是哪一种字体呢 ? 我想在Xcode里也用这种字体 , 因为这种字体看着挺舒服的 .
  • TA的每日心情
    奋斗
    2022-12-13 21:26
  • 签到天数: 371 天

    [LV.9]以坛为家II

     楼主| 发表于 2015-4-24 13:04:16 | 显示全部楼层
    本帖最后由 Sian 于 2015-4-24 13:15 编辑
    喵了个咪 发表于 2015-4-24 10:06
    那请问系统用的是哪一种字体呢 ? 我想在Xcode里也用这种字体 , 因为这种字体看着挺舒服的 .
    Courier New
    Consolas
  • TA的每日心情
    恶心
    2015-11-23 14:20
  • 签到天数: 92 天

    [LV.6]常住居民II

    发表于 2015-4-24 13:13:35 | 显示全部楼层

    非常感谢 !! {:3_48:}
  • TA的每日心情
    奋斗
    2022-12-13 21:26
  • 签到天数: 371 天

    [LV.9]以坛为家II

     楼主| 发表于 2015-4-24 13:16:04 | 显示全部楼层

    Consolas
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    手机版|小黑屋|Archiver|iOS开发笔记 ( 湘ICP备14010846号 )

    GMT+8, 2024-5-4 12:51 , Processed in 0.054430 second(s), 25 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表