iOS使用视听媒体框架AVFoundation实现照片拍摄
程序员文章站
2023-10-24 09:22:28
用系统自带的视听媒体的框架,avfoundation实现照片拍摄。相比uikit框架(uiimagepickercontroller高度封装),avfoundation框架...
用系统自带的视听媒体的框架,avfoundation实现照片拍摄。相比uikit框架(uiimagepickercontroller高度封装),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下载链接
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 果脯的制作方法
下一篇: iOS实现截取字符串中汉字功能