iOS使用视听媒体框架AVFoundation实现照片拍摄

用系统自带的视听媒体的框架,AVFoundation实现照片拍摄。相比UIKit框架(UIImagePickerController高度封装),AVFoundation框架让开发者有更大的发挥空间。

首先看一下效果图:

iOS使用视听媒体框架AVFoundation实现照片拍摄

下面贴上核心控制器代码:

#import "HWPhotoVC.h"
#import <AVFoundation/AVFoundation.h>

@interface HWPhotoVC ()

@property (nonatomic,strong) AVCaptureSession *captureSession;//负责输入和输出设备之间的数据传递
@property (nonatomic,strong) AVCaptureDeviceInput *captureDeviceInput;//负责从AVCaptureDevice获得输入数据
@property (nonatomic,strong) AVCaptureStillImageOutput *captureStillImageOutput;//照片输出流
@property (nonatomic,strong) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;//相机拍摄预览图层
@property (nonatomic,weak) UIView *containerView;//内容视图
@property (nonatomic,weak) UIImageView *focusCursor;//聚焦按钮
@property (nonatomic,weak) UIImageView *imgView;//拍摄照片

@end

@implementation HWPhotoVC

- (void)viewDidLoad {
 [super viewDidLoad];

 self.navigationItem.title = @"拍照";
 self.view.backgroundColor = [UIColor whiteColor];

 //创建控件
 [self creatControl];
}

- (void)viewWillAppear:(BOOL)animated
{
 [super viewWillAppear:animated];

 //初始化信息
 [self initPhotoInfo];
}

- (void)viewDidAppear:(BOOL)animated
{
 [super viewDidAppear:animated];

 [self.captureSession startRunning];
}

- (void)viewDidDisappear:(BOOL)animated
{
 [super viewDidDisappear:animated];

 [self.captureSession stopRunning];
}

- (void)creatControl
{
 CGFloat btnW = 150.f;
 CGFloat btnH = 40.f;
 CGFloat marginY = 20.f;
 CGFloat w = [UIScreen mainScreen].bounds.size.width;
 CGFloat h = [UIScreen mainScreen].bounds.size.height;

 //内容视图
 CGFloat containerViewH = h - 64 - btnH - marginY * 3;
 UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(10,64 + marginY,w - 20,containerViewH)];
 containerView.backgroundColor = [UIColor whiteColor];
 containerView.layer.borderWidth = 1.f;
 containerView.layer.borderColor = [[UIColor grayColor] CGColor];
 [self.view addSubview:containerView];
 _containerView = containerView;

 //摄像头切换按钮
 CGFloat cameraSwitchBtnW = 50.f;
 CGFloat cameraSwitchBtnMargin = 10.f;
 UIButton *cameraSwitchBtn = [[UIButton alloc] initWithFrame:CGRectMake(containerView.bounds.size.width - cameraSwitchBtnW - cameraSwitchBtnMargin,64 + marginY + cameraSwitchBtnMargin,cameraSwitchBtnW,cameraSwitchBtnW)];
 [cameraSwitchBtn setImage:[UIImage imageNamed:@"camera_switch"] forState:UIControlStateNormal];
 [cameraSwitchBtn addTarget:self action:@selector(cameraSwitchBtnOnClick) forControlEvents:UIControlEventTouchUpInside];
 [self.view addSubview:cameraSwitchBtn];

 //聚焦图片
 UIImageView *focusCursor = [[UIImageView alloc] initWithFrame:CGRectMake(50,50,75,75)];
 focusCursor.alpha = 0;
 focusCursor.image = [UIImage imageNamed:@"camera_focus_red"];
 [containerView addSubview:focusCursor];
 _focusCursor = focusCursor;

 //拍摄照片容器
 UIImageView *imgView = [[UIImageView alloc] initWithFrame:containerView.frame];
 imgView.hidden = YES;
 imgView.layer.borderWidth = 1.f;
 imgView.layer.borderColor = [[UIColor grayColor] CGColor];
 imgView.contentMode = UIViewContentModeScaleAspectFill;
 imgView.clipsToBounds = YES;
 [self.view addSubview:imgView];
 _imgView = imgView;

 //按钮
 NSArray *titleArray = @[@"拍摄照片",@"重新拍摄"];
 CGFloat btnY = CGRectGetMaxY(containerView.frame) + marginY;
 CGFloat margin = (w - btnW * titleArray.count) / (titleArray.count + 1);
 for (int i = 0; i < titleArray.count; i++) {
  CGFloat btnX = margin + (margin + btnW) * i;
  UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(btnX,btnY,btnW,btnH)];
  btn.tag = 1000 + i;
  [btn setTitle:titleArray[i] forState:UIControlStateNormal];
  btn.backgroundColor = [UIColor orangeColor];
  btn.layer.cornerRadius = 2.0f;
  btn.layer.masksToBounds = YES;
  if (i == 1) {
   btn.hidden = YES;
  }
  [btn addTarget:self action:@selector(btnOnClick:) forControlEvents:UIControlEventTouchUpInside];
  [self.view addSubview:btn];
 }
}

- (void)initPhotoInfo
{
 //初始化会话
 _captureSession = [[AVCaptureSession alloc] init];

 //设置分辨率
 if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
  _captureSession.sessionPreset = 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 *outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG};
 //输出设置
 [_captureStillImageOutput setOutputSettings:outputSettings];

 //将设备输入添加到会话中
 if ([_captureSession canAddInput:_captureDeviceInput]) {
  [_captureSession addInput:_captureDeviceInput];
 }

 //将设备输出添加到会话中
 if ([_captureSession canAddOutput:_captureStillImageOutput]) {
  [_captureSession addOutput:_captureStillImageOutput];
 }

 //创建视频预览层,用于实时展示摄像头状态
 _captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.captureSession];

 //摄像头方向
 AVCaptureConnection *captureConnection = [self.captureVideoPreviewLayer connection];
 captureConnection.videoOrientation = AVCaptureVideoOrientationPortrait;

 CALayer *layer = _containerView.layer;
 layer.masksToBounds = YES;

 _captureVideoPreviewLayer.frame = layer.bounds;
 //填充模式
 _captureVideoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
 //将视频预览层添加到界面中
 [layer insertSublayer:_captureVideoPreviewLayer below:self.focusCursor.layer];

 [self addNotificationToCaptureDevice:captureDevice];
 [self addGenstureRecognizer];
}

- (void)btnOnClick:(UIButton *)btn
{
 if (btn.tag == 1000) {
  //拍摄照片
  [self photoBtnOnClick];

 }else if (btn.tag == 1001) {
  //重新拍摄
  [self resetPhoto];
 }
}

#pragma mark 拍照
- (void)photoBtnOnClick
{
 //根据设备输出获得连接
 AVCaptureConnection *captureConnection = [self.captureStillImageOutput connectionWithMediaType:AVMediaTypeVideo];
 captureConnection.videoOrientation = AVCaptureVideoOrientationPortrait;

 //根据连接取得设备输出的数据
 [self.captureStillImageOutput captureStillImageAsynchronouslyFromConnection:captureConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer,NSError *error) {
  if (imageDataSampleBuffer) {
   NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
   UIImage *image = [UIImage imageWithData:imageData];
   _imgView.image = image;
   _imgView.hidden = NO;
  }
 }];

 UIButton *btn = (UIButton *)[self.view viewWithTag:1001];
 btn.hidden = NO;
}

//重新拍摄
- (void)resetPhoto
{
 _imgView.hidden = YES;
 UIButton *btn = (UIButton *)[self.view viewWithTag:1001];
 btn.hidden = YES;
}

#pragma mark - 通知
//给输入设备添加通知
- (void)addNotificationToCaptureDevice:(AVCaptureDevice *)captureDevice
{
 //注意添加区域改变捕获通知必须首先设置设备允许捕获
 [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
  captureDevice.subjectAreaChangeMonitoringEnabled = YES;
 }];

 //捕获区域发生改变
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(areaChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
}

- (void)removeNotificationFromCaptureDevice:(AVCaptureDevice *)captureDevice
{
 [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:captureDevice];
}

//移除所有通知
- (void)removeNotification
{
 [[NSNotificationCenter defaultCenter] removeObserver:self];
}

//设备连接成功
- (void)deviceConnected:(NSNotification *)notification
{
 NSLog(@"设备已连接...");
}

//设备连接断开
- (void)deviceDisconnected:(NSNotification *)notification
{
 NSLog(@"设备已断开.");
}

//捕获区域改变
- (void)areaChange:(NSNotification *)notification
{
 NSLog(@"捕获区域改变...");
}

#pragma mark - 私有方法
//取得指定位置的摄像头
- (AVCaptureDevice *)getCameraDeviceWithPosition:(AVCaptureDevicePosition )position
{
 NSArray *cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
 for (AVCaptureDevice *camera in cameras) {
  if ([camera position] == position) {
   return camera;
  }
 }

 return nil;
}

#pragma mark 切换前后摄像头
- (void)cameraSwitchBtnOnClick
{
 AVCaptureDevice *currentDevice = [self.captureDeviceInput device];
 AVCaptureDevicePosition currentPosition = [currentDevice position];
 [self removeNotificationFromCaptureDevice:currentDevice];

 AVCaptureDevice *toChangeDevice;
 AVCaptureDevicePosition toChangePosition = AVCaptureDevicePositionFront;
 if (currentPosition == AVCaptureDevicePositionUnspecified || currentPosition == AVCaptureDevicePositionFront) {
  toChangePosition = AVCaptureDevicePositionBack;
 }
 toChangeDevice = [self getCameraDeviceWithPosition:toChangePosition];
 [self addNotificationToCaptureDevice:toChangeDevice];
 //获得要调整的设备输入对象
 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];
}

//改变设备属性的统一操作方法
- (void)changeDeviceProperty:(void (^)(AVCaptureDevice *))propertyChange
{
 AVCaptureDevice *captureDevice = [self.captureDeviceInput device];
 NSError *error;
 //注意改变设备属性前一定要首先调用lockForConfiguration:调用完之后使用unlockForConfiguration方法解锁
 if ([captureDevice lockForConfiguration:&error]) {
  propertyChange(captureDevice);
  [captureDevice unlockForConfiguration];

 }else {
  NSLog(@"设置设备属性过程发生错误,错误信息:%@",error.localizedDescription);
 }
}

//设置闪光灯模式
- (void)setFlashMode:(AVCaptureFlashMode)flashMode
{
 [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
  if ([captureDevice isFlashModeSupported:flashMode]) {
   [captureDevice setFlashMode:flashMode];
  }
 }];
}

//设置聚焦模式
- (void)setFocusMode:(AVCaptureFocusMode)focusMode
{
 [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
  if ([captureDevice isFocusModeSupported:focusMode]) {
   [captureDevice setFocusMode:focusMode];
  }
 }];
}

//设置曝光模式
- (void)setExposureMode:(AVCaptureExposureMode)exposureMode
{
 [self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
  if ([captureDevice isExposureModeSupported:exposureMode]) {
   [captureDevice setExposureMode:exposureMode];
  }
 }];
}

//设置聚焦点
- (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];
  }
 }];
}

//添加点按手势,点按时聚焦
- (void)addGenstureRecognizer
{
 [self.containerView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapScreen:)]];
}

- (void)tapScreen:(UITapGestureRecognizer *)tapGesture
{
 CGPoint point = [tapGesture locationInView:self.containerView];
 //将UI坐标转化为摄像头坐标
 CGPoint cameraPoint = [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];
 [self setFocusCursorWithPoint:point];
 [self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint];
}

//设置聚焦光标位置
- (void)setFocusCursorWithPoint:(CGPoint)point
{
 self.focusCursor.center = point;
 self.focusCursor.transform = CGAffineTransformMakeScale(1.5,1.5);
 self.focusCursor.alpha = 1.0;
 [UIView animateWithDuration:1.0 animations:^{
  self.focusCursor.transform = CGAffineTransformIdentity;
 } completion:^(BOOL finished) {
  self.focusCursor.alpha = 0;
 }];
}

- (void)dealloc
{
 [self removeNotification];
}

@end

Demo下载链接

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


当我们远离最新的 iOS 16 更新版本时,我们听到了困扰 Apple 最新软件的错误和性能问题。
欧版/美版 特别说一下,美版选错了 可能会永久丧失4G,不过只有5%的概率会遇到选择运营商界面且部分必须连接到iTunes才可以激活
一般在接外包的时候, 通常第三方需要安装你的app进行测试(这时候你的app肯定是还没传到app store之前)。
前言为了让更多的人永远记住12月13日,各大厂都在这一天将应用变灰了。那么接下来我们看一下Flutter是如何实现的。Flutter中实现整个App变为灰色在Flutter中实现整个App变为灰色是非常简单的,只需要在最外层的控件上包裹ColorFiltered,用法如下:ColorFiltered(颜色过滤器)看名字就知道是增加颜色滤镜效果的,ColorFiltered( colorFilter:ColorFilter.mode(Colors.grey, BlendMode.
flutter升级/版本切换
(1)在C++11标准时,open函数的文件路径可以传char指针也可以传string指针,而在C++98标准,open函数的文件路径只能传char指针;(2)open函数的第二个参数是打开文件的模式,从函数定义可以看出,如果调用open函数时省略mode模式参数,则默认按照可读可写(ios_base:in | ios_base::out)的方式打开;(3)打开文件时的mode的模式是从内存的角度来定义的,比如:in表示可读,就是从文件读数据往内存读写;out表示可写,就是把内存数据写到文件中;
文章目录方法一:分别将图片和文字置灰UIImage转成灰度图UIColor转成灰度颜色方法二:给App整体添加灰色滤镜参考App页面置灰,本质是将彩色图像转换为灰度图像,本文提供两种方法实现,一种是App整体置灰,一种是单个页面置灰,可结合具体的业务场景使用。方法一:分别将图片和文字置灰一般情况下,App页面的颜色深度是24bit,也就是RGB各8bit;如果算上Alpha通道的话就是32bit,RGBA(或者ARGB)各8bit。灰度图像的颜色深度是8bit,这8bit表示的颜色不是彩色,而是256
领导让调研下黑(灰)白化实现方案,自己调研了两天,根据网上资料,做下记录只是学习过程中的记录,还是写作者牛逼
让学前端不再害怕英语单词(二),通过本文,可以对css,js和es6的单词进行了在逻辑上和联想上的记忆,让初学者更快的上手前端代码
用Python送你一颗跳动的爱心
在uni-app项目中实现人脸识别,既使用uni-app中的live-pusher开启摄像头,创建直播推流。通过快照截取和压缩图片,以base64格式发往后端。
商户APP调用微信提供的SDK调用微信支付模块,商户APP会跳转到微信中完成支付,支付完后跳回到商户APP内,最后展示支付结果。CSDN前端领域优质创作者,资深前端开发工程师,专注前端开发,在CSDN总结工作中遇到的问题或者问题解决方法以及对新技术的分享,欢迎咨询交流,共同学习。),验证通过打开选择支付方式弹窗页面,选择微信支付或者支付宝支付;4.可取消支付,放弃支付会返回会员页面,页面提示支付取消;2.判断支付方式,如果是1,则是微信支付方式。1.判断是否在微信内支付,需要在微信外支付。
Mac命令行修改ipa并重新签名打包
首先在 iOS 设备中打开开发者模式。位于:设置 - 隐私&安全 - 开发者模式(需重启)
一 现象导入MBProgressHUD显示信息时,出现如下异常现象Undefined symbols for architecture x86_64: "_OBJC_CLASS_$_MBProgressHUD", referenced from: objc-class-ref in ViewController.old: symbol(s) not found for architecture x86_64clang: error: linker command failed wit
Profiles >> 加号添加 >> Distribution >> "App Store" >> 选择 2.1 创建的App ID >> 选择绑定 2.3 的发布证书(.cer)>> 输入描述文件名称 >> Generate 生成描述文件 >> Download。Certificates >> 加号添加 >> "App Store and Ad Hoc" >> “Choose File...” >> 选择上一步生成的证书请求文件 >> Continue >> Download。
今天有需求,要实现的功能大致如下:在安卓和ios端实现分享功能可以分享链接,图片,文字,视频,文件,等欢迎大佬多多来给萌新指正,欢迎大家来共同探讨。如果各位看官觉得文章有点点帮助,跪求各位给点个“一键三连”,谢啦~声明:本博文章若非特殊注明皆为原创原文链接。