iOS 开发 -------- 自定义系统相机

本贴最后更新于 2225 天前,其中的信息可能已经时过境迁

一.初衷

看到各种 APP 的自定义相机界面,之后再看我们自己的 APP 全是用的系统相机,感觉有点遗憾,就简单写了一下.

二.简单代码(用的是 AVFoundation)

#import "CustomCameraViewController.h"
#import <AVFoundation/AVFoundation.h>
#import "PhotoHandleViewController.h"
@interface CustomCameraViewController ()<AVCaptureMetadataOutputObjectsDelegate,AVCapturePhotoCaptureDelegate>
// 捕获设备,前置,后置摄像头,麦克风
@property (nonatomic,strong) AVCaptureDevice *device;
@property (nonatomic,strong) AVCapturePhotoSettings *settings;
// 输入设备
@property (nonatomic,strong) AVCaptureDeviceInput *input;
@property (nonatomic,strong) AVCaptureMetadataOutput *output;
// 输出图片
@property (nonatomic,strong) AVCapturePhotoOutput *photoOutput;
// 摄像头
@property (nonatomic,strong) AVCaptureSession *session;
// 实时显示捕获的图像
@property (nonatomic,strong) AVCaptureVideoPreviewLayer *layer;
// 聚焦点
@property (nonatomic,strong) UIView *focusView;
@end

初始化自定义相机

- (void)customCamera {
    // AVMediaTypeVideo 代表视频 (默认使用后置);
    self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    
    // 设备输入
    self.input = [[AVCaptureDeviceInput alloc] initWithDevice:self.device error:nil];
    
    // 输出对象
    self.output = [[AVCaptureMetadataOutput alloc] init];
    self.photoOutput = [[AVCapturePhotoOutput alloc] init];
    
    // 会话 结合输入输出
    self.session = [[AVCaptureSession alloc] init];
    
    if ([self.session canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
        
        self.session.sessionPreset = AVCaptureSessionPreset1280x720;
    }
    
    if ([self.session canAddInput:self.input]) {
        
        [self.session addInput:self.input];
    }
    
    if ([self.session canAddOutput:self.photoOutput]) {
        
        [self.session addOutput:self.photoOutput];
    }
    
    // 预览
    
    self.layer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
    self.layer.frame = CGRectMake(20, 70, self.view.frame.size.width - 40, self.view.frame.size.height / 2);
    self.layer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    
    [self.view.layer addSublayer:self.layer];
    
    // 开始启动
    [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];
    }
}

聚焦点

// 聚焦点

- (void)focusGesture:(UITapGestureRecognizer *)recognizer {
    
    CGPoint point = [recognizer locationInView:recognizer.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];
        
    }
    _focusView.center = point;
    _focusView.hidden = NO;
    
    [UIView animateWithDuration:0.3 animations:^{
        
        _focusView.transform = CGAffineTransformMakeScale(1.25, 1.25);
    } completion:^(BOOL finished) {
        
        [UIView animateWithDuration:0.5 animations:^{
            _focusView.transform = CGAffineTransformIdentity;
        } completion:^(BOOL finished) {
           
            _focusView.hidden = YES;
        }];
    }];
}

截取图片

- (void)shutterCamera {
    
    AVCaptureConnection *videoConnection = [self.photoOutput connectionWithMediaType:AVMediaTypeVideo];
    if (!videoConnection) {
        
        NSLog(@"拍照失败");
        return;
    }
    
    [self.photoOutput capturePhotoWithSettings:self.settings delegate:self];
    
}

- (void)captureOutput:(AVCapturePhotoOutput *)captureOutput didFinishProcessingPhotoSampleBuffer:(nullable CMSampleBufferRef)photoSampleBuffer previewPhotoSampleBuffer:(nullable CMSampleBufferRef)previewPhotoSampleBuffer resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings bracketSettings:(nullable AVCaptureBracketedStillImageSettings *)bracketSettings error:(nullable NSError *)error {
    
    if (photoSampleBuffer == NULL) {
        
        return;
    }
    
    NSData *imgData = [AVCapturePhotoOutput JPEGPhotoDataRepresentationForJPEGSampleBuffer:photoSampleBuffer previewPhotoSampleBuffer:previewPhotoSampleBuffer];
    // 照片处理界面
    PhotoHandleViewController *photoVC = [[PhotoHandleViewController alloc] init];
    photoVC.img = [UIImage imageWithData:imgData];
    [self.navigationController pushViewController:photoVC animated:YES];
}

照片处理 PhotoHandleViewController

#import "PhotoHandleViewController.h"
#import "CustomCollectionViewCell.h"

@interface PhotoHandleViewController () <UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout>
@property (nonatomic,strong) UICollectionView *bottomC;
@property (nonatomic,strong) NSMutableArray *filterArray;
@property (nonatomic,strong) UIImageView *imgV;
@end
self.imgV = [[UIImageView alloc] initWithFrame:CGRectMake(20, 70, self.view.frame.size.width - 40, self.view.frame.size.height / 2)];
    self.imgV.image = self.img;
    self.imgV.contentMode = UIViewContentModeScaleAspectFill;
    self.imgV.clipsToBounds = YES;
    [self.view addSubview:self.imgV];

// 滤镜效果
    self.filterArray = [[NSMutableArray alloc] initWithObjects:
                        @"OriginImage",
                        @"CIPhotoEffectChrome",
                        @"CIPhotoEffectFade",
                        @"CIPhotoEffectInstant",
                        @"CIPhotoEffectProcess",
                        @"CIPhotoEffectTransfer",
                        @"CISRGBToneCurveToLinear",
                        @"CIColorInvert",
                        @"CIColorPosterize",
                        @"CIFalseColor",
                        @"CIXRay",
                        @"CIThermal",
                        @"CISepiaTone",
                        @"CIColorMonochrome",
                        nil];
    
    UICollectionViewFlowLayout *flowLay = [[UICollectionViewFlowLayout alloc] init];
    flowLay.itemSize = CGSizeMake(100, 150);
    flowLay.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    self.bottomC = [[UICollectionView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(self.imgV.frame) + 20, self.view.frame.size.width, 200) collectionViewLayout:flowLay];
    self.bottomC.backgroundColor = [UIColor whiteColor];
    self.bottomC.delegate = self;
    self.bottomC.dataSource = self;
    [self.view addSubview:self.bottomC];
    
    [self.bottomC registerClass:[CustomCollectionViewCell class] forCellWithReuseIdentifier:@"cell"];

处理滤镜

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    
    [self fliterEvent:self.filterArray[indexPath.item]];
}

#pragma mark 滤镜处理事件

- (void)fliterEvent:(NSString *)filterName
{
    if ([filterName isEqualToString:@"OriginImage"]) {
        self.imgV.image = self.img;
        
    }else{
        //将UIImage转换成CIImage
        CIImage *ciImage = [[CIImage alloc] initWithImage:[self fixOrientation:self.img]];
        
        //创建滤镜
        CIFilter *filter = [CIFilter filterWithName:filterName keysAndValues:kCIInputImageKey, ciImage, nil];
        
        //已有的值不改变,其他的设为默认值
        [filter setDefaults];
        
        //获取绘制上下文
        CIContext *context = [CIContext contextWithOptions:nil];
        
        //渲染并输出CIImage
        CIImage *outputImage = [filter outputImage];
        
        //创建CGImage句柄
        CGImageRef cgImage = [context createCGImage:outputImage fromRect:[outputImage extent]];
        
        //获取图片
        UIImage *image = [UIImage imageWithCGImage:cgImage];
        
        //释放CGImage句柄
        CGImageRelease(cgImage);
        
        self.imgV.image = image;
    }
}

此非常之重要,由于通过 cgimageref 得到的图片 会逆时针转 90 度的,因此用以下方法得到正确图片

#pragma mark -------

- (UIImage *)fixOrientation:(UIImage *)aImage {
    
    // No-op if the orientation is already correct
    if (aImage.imageOrientation == UIImageOrientationUp)
        return aImage;
    
    // We need to calculate the proper transformation to make the image upright.
    // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
    CGAffineTransform transform = CGAffineTransformIdentity;
    
    switch (aImage.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;
            
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
            transform = CGAffineTransformRotate(transform, M_PI_2);
            break;
            
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
            transform = CGAffineTransformRotate(transform, -M_PI_2);
            break;
        default:
            break;
    }
    
    switch (aImage.imageOrientation) {
        case UIImageOrientationUpMirrored:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
            
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
        default:
            break;
    }
    
    // Now we draw the underlying CGImage into a new context, applying the transform
    // calculated above.
    CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
                                             CGImageGetBitsPerComponent(aImage.CGImage), 0,
                                             CGImageGetColorSpace(aImage.CGImage),
                                             CGImageGetBitmapInfo(aImage.CGImage));
    CGContextConcatCTM(ctx, transform);
    switch (aImage.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            // Grr...
            CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
            break;
            
        default:
            CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
            break;
    }
    
    // And now we just create a new UIImage from the drawing context
    CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
    UIImage *img = [UIImage imageWithCGImage:cgimg];
    CGContextRelease(ctx);
    CGImageRelease(cgimg);
    return img;
}

最后附上 demo 地址:https://github.com/HuixiaZhang/CustomCamera

  • iOS

    iOS 是由苹果公司开发的移动操作系统,最早于 2007 年 1 月 9 日的 Macworld 大会上公布这个系统,最初是设计给 iPhone 使用的,后来陆续套用到 iPod touch、iPad 以及 Apple TV 等产品上。iOS 与苹果的 Mac OS X 操作系统一样,属于类 Unix 的商业操作系统。

    85 引用 • 139 回帖 • 5 关注
  • oc
    2 引用
  • 相机
    2 引用

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • ZeroNet

    ZeroNet 是一个基于比特币加密技术和 BT 网络技术的去中心化的、开放开源的网络和交流系统。

    1 引用 • 21 回帖 • 636 关注
  • 京东

    京东是中国最大的自营式电商企业,2015 年第一季度在中国自营式 B2C 电商市场的占有率为 56.3%。2014 年 5 月,京东在美国纳斯达克证券交易所正式挂牌上市(股票代码:JD),是中国第一个成功赴美上市的大型综合型电商平台,与腾讯、百度等中国互联网巨头共同跻身全球前十大互联网公司排行榜。

    14 引用 • 102 回帖 • 377 关注
  • 房星科技

    房星网,我们不和没有钱的程序员谈理想,我们要让程序员又有理想又有钱。我们有雄厚的房地产行业线下资源,遍布昆明全城的 100 家门店、四千地产经纪人是我们坚实的后盾。

    6 引用 • 141 回帖 • 586 关注
  • uTools

    uTools 是一个极简、插件化、跨平台的现代桌面软件。通过自由选配丰富的插件,打造你得心应手的工具集合。

    6 引用 • 14 回帖 • 1 关注
  • Vditor

    Vditor 是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。它使用 TypeScript 实现,支持原生 JavaScript、Vue、React 和 Angular。

    349 引用 • 1803 回帖
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    4 引用 • 16 回帖 • 4 关注
  • V2EX

    V2EX 是创意工作者们的社区。这里目前汇聚了超过 400,000 名主要来自互联网行业、游戏行业和媒体行业的创意工作者。V2EX 希望能够成为创意工作者们的生活和事业的一部分。

    17 引用 • 236 回帖 • 325 关注
  • Postman

    Postman 是一款简单好用的 HTTP API 调试工具。

    4 引用 • 3 回帖
  • 锤子科技

    锤子科技(Smartisan)成立于 2012 年 5 月,是一家制造移动互联网终端设备的公司,公司的使命是用完美主义的工匠精神,打造用户体验一流的数码消费类产品(智能手机为主),改善人们的生活质量。

    4 引用 • 31 回帖 • 3 关注
  • HBase

    HBase 是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的 Google 论文 “Bigtable:一个结构化数据的分布式存储系统”。就像 Bigtable 利用了 Google 文件系统所提供的分布式数据存储一样,HBase 在 Hadoop 之上提供了类似于 Bigtable 的能力。

    17 引用 • 6 回帖 • 71 关注
  • MyBatis

    MyBatis 本是 Apache 软件基金会 的一个开源项目 iBatis,2010 年这个项目由 Apache 软件基金会迁移到了 google code,并且改名为 MyBatis ,2013 年 11 月再次迁移到了 GitHub。

    170 引用 • 414 回帖 • 385 关注
  • Mac

    Mac 是苹果公司自 1984 年起以“Macintosh”开始开发的个人消费型计算机,如:iMac、Mac mini、Macbook Air、Macbook Pro、Macbook、Mac Pro 等计算机。

    166 引用 • 595 回帖
  • PostgreSQL

    PostgreSQL 是一款功能强大的企业级数据库系统,在 BSD 开源许可证下发布。

    22 引用 • 22 回帖 • 1 关注
  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1063 引用 • 3453 回帖 • 201 关注
  • Ngui

    Ngui 是一个 GUI 的排版显示引擎和跨平台的 GUI 应用程序开发框架,基于
    Node.js / OpenGL。目标是在此基础上开发 GUI 应用程序可拥有开发 WEB 应用般简单与速度同时兼顾 Native 应用程序的性能与体验。

    7 引用 • 9 回帖 • 388 关注
  • Quicker

    Quicker 您的指尖工具箱!操作更少,收获更多!

    31 引用 • 124 回帖 • 4 关注
  • Vim

    Vim 是类 UNIX 系统文本编辑器 Vi 的加强版本,加入了更多特性来帮助编辑源代码。Vim 的部分增强功能包括文件比较(vimdiff)、语法高亮、全面的帮助系统、本地脚本(Vimscript)和便于选择的可视化模式。

    29 引用 • 66 回帖 • 2 关注
  • 自由行
    10 关注
  • 创业

    你比 99% 的人都优秀么?

    84 引用 • 1399 回帖
  • 新人

    让我们欢迎这对新人。哦,不好意思说错了,让我们欢迎这位新人!
    新手上路,请谨慎驾驶!

    52 引用 • 228 回帖 • 1 关注
  • 服务器

    服务器,也称伺服器,是提供计算服务的设备。由于服务器需要响应服务请求,并进行处理,因此一般来说服务器应具备承担服务并且保障服务的能力。

    125 引用 • 588 回帖
  • 宕机

    宕机,多指一些网站、游戏、网络应用等服务器一种区别于正常运行的状态,也叫“Down 机”、“当机”或“死机”。宕机状态不仅仅是指服务器“挂掉了”、“死机了”状态,也包括服务器假死、停用、关闭等一些原因而导致出现的不能够正常运行的状态。

    13 引用 • 82 回帖 • 53 关注
  • Sandbox

    如果帖子标签含有 Sandbox ,则该帖子会被视为“测试帖”,主要用于测试社区功能,排查 bug 等,该标签下内容不定期进行清理。

    406 引用 • 1246 回帖 • 579 关注
  • Hprose

    Hprose 是一款先进的轻量级、跨语言、跨平台、无侵入式、高性能动态远程对象调用引擎库。它不仅简单易用,而且功能强大。你无需专门学习,只需看上几眼,就能用它轻松构建分布式应用系统。

    9 引用 • 17 回帖 • 612 关注
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    8044 引用 • 36762 回帖 • 162 关注
  • 面试

    面试造航母,上班拧螺丝。多面试,少加班。

    325 引用 • 1395 回帖
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 761 关注