欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2023-10-24 09:22:28
用系统自带的视听媒体的框架,avfoundation实现照片拍摄。相比uikit框架(uiimagepickercontroller高度封装),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下载链接

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