iOS使用視聽媒體框架AVFoundation實現(xiàn)照片拍攝
更新時間:2019年04月28日 11:38:11 作者:hero_wqb
這篇文章主要為大家詳細(xì)介紹了iOS使用視聽媒體框架AVFoundation實現(xiàn)照片拍攝,具有一定的參考價值,感興趣的小伙伴們可以參考一下
用系統(tǒng)自帶的視聽媒體的框架,AVFoundation實現(xiàn)照片拍攝。相比UIKit框架(UIImagePickerController高度封裝),AVFoundation框架讓開發(fā)者有更大的發(fā)揮空間。
首先看一下效果圖:

下面貼上核心控制器代碼:
#import "HWPhotoVC.h"
#import <AVFoundation/AVFoundation.h>
@interface HWPhotoVC ()
@property (nonatomic, strong) AVCaptureSession *captureSession;//負(fù)責(zé)輸入和輸出設(shè)備之間的數(shù)據(jù)傳遞
@property (nonatomic, strong) AVCaptureDeviceInput *captureDeviceInput;//負(fù)責(zé)從AVCaptureDevice獲得輸入數(shù)據(jù)
@property (nonatomic, strong) AVCaptureStillImageOutput *captureStillImageOutput;//照片輸出流
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *captureVideoPreviewLayer;//相機(jī)拍攝預(yù)覽圖層
@property (nonatomic, weak) UIView *containerView;//內(nèi)容視圖
@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];
//創(chuàng)建控件
[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;
//內(nèi)容視圖
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];
//設(shè)置分辨率
if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
_captureSession.sessionPreset = AVCaptureSessionPreset1280x720;
}
//獲得輸入設(shè)備,取得后置攝像頭
AVCaptureDevice *captureDevice = [self getCameraDeviceWithPosition:AVCaptureDevicePositionBack];
if (!captureDevice) {
NSLog(@"取得后置攝像頭時出現(xiàn)問題");
return;
}
NSError *error = nil;
//根據(jù)輸入設(shè)備初始化設(shè)備輸入對象,用于獲得輸入數(shù)據(jù)
_captureDeviceInput = [[AVCaptureDeviceInput alloc]initWithDevice:captureDevice error:&error];
if (error) {
NSLog(@"取得設(shè)備輸入對象時出錯,錯誤原因:%@", error.localizedDescription);
return;
}
//初始化設(shè)備輸出對象,用于獲得輸出數(shù)據(jù)
_captureStillImageOutput = [[AVCaptureStillImageOutput alloc] init];
NSDictionary *outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG};
//輸出設(shè)置
[_captureStillImageOutput setOutputSettings:outputSettings];
//將設(shè)備輸入添加到會話中
if ([_captureSession canAddInput:_captureDeviceInput]) {
[_captureSession addInput:_captureDeviceInput];
}
//將設(shè)備輸出添加到會話中
if ([_captureSession canAddOutput:_captureStillImageOutput]) {
[_captureSession addOutput:_captureStillImageOutput];
}
//創(chuàng)建視頻預(yù)覽層,用于實時展示攝像頭狀態(tài)
_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;
//將視頻預(yù)覽層添加到界面中
[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
{
//根據(jù)設(shè)備輸出獲得連接
AVCaptureConnection *captureConnection = [self.captureStillImageOutput connectionWithMediaType:AVMediaTypeVideo];
captureConnection.videoOrientation = AVCaptureVideoOrientationPortrait;
//根據(jù)連接取得設(shè)備輸出的數(shù)據(jù)
[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 - 通知
//給輸入設(shè)備添加通知
- (void)addNotificationToCaptureDevice:(AVCaptureDevice *)captureDevice
{
//注意添加區(qū)域改變捕獲通知必須首先設(shè)置設(shè)備允許捕獲
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
captureDevice.subjectAreaChangeMonitoringEnabled = YES;
}];
//捕獲區(qū)域發(fā)生改變
[[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];
}
//設(shè)備連接成功
- (void)deviceConnected:(NSNotification *)notification
{
NSLog(@"設(shè)備已連接...");
}
//設(shè)備連接斷開
- (void)deviceDisconnected:(NSNotification *)notification
{
NSLog(@"設(shè)備已斷開.");
}
//捕獲區(qū)域改變
- (void)areaChange:(NSNotification *)notification
{
NSLog(@"捕獲區(qū)域改變...");
}
#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];
//獲得要調(diào)整的設(shè)備輸入對象
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];
}
//改變設(shè)備屬性的統(tǒng)一操作方法
- (void)changeDeviceProperty:(void (^)(AVCaptureDevice *))propertyChange
{
AVCaptureDevice *captureDevice = [self.captureDeviceInput device];
NSError *error;
//注意改變設(shè)備屬性前一定要首先調(diào)用lockForConfiguration:調(diào)用完之后使用unlockForConfiguration方法解鎖
if ([captureDevice lockForConfiguration:&error]) {
propertyChange(captureDevice);
[captureDevice unlockForConfiguration];
}else {
NSLog(@"設(shè)置設(shè)備屬性過程發(fā)生錯誤,錯誤信息:%@", error.localizedDescription);
}
}
//設(shè)置閃光燈模式
- (void)setFlashMode:(AVCaptureFlashMode)flashMode
{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isFlashModeSupported:flashMode]) {
[captureDevice setFlashMode:flashMode];
}
}];
}
//設(shè)置聚焦模式
- (void)setFocusMode:(AVCaptureFocusMode)focusMode
{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isFocusModeSupported:focusMode]) {
[captureDevice setFocusMode:focusMode];
}
}];
}
//設(shè)置曝光模式
- (void)setExposureMode:(AVCaptureExposureMode)exposureMode
{
[self changeDeviceProperty:^(AVCaptureDevice *captureDevice) {
if ([captureDevice isExposureModeSupported:exposureMode]) {
[captureDevice setExposureMode:exposureMode];
}
}];
}
//設(shè)置聚焦點
- (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坐標(biāo)轉(zhuǎn)化為攝像頭坐標(biāo)
CGPoint cameraPoint = [self.captureVideoPreviewLayer captureDevicePointOfInterestForPoint:point];
[self setFocusCursorWithPoint:point];
[self focusWithMode:AVCaptureFocusModeAutoFocus exposureMode:AVCaptureExposureModeAutoExpose atPoint:cameraPoint];
}
//設(shè)置聚焦光標(biāo)位置
- (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下載鏈接
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
IOS開發(fā)教程之put上傳文件的服務(wù)器的配置及實例分享
IOS開發(fā)給人一種高大上的趕腳,弄的好像你不會IOS就不是個合格的程序員一樣,好吧,既然是大趨勢,那我們就來學(xué)習(xí)下吧,今天來看看put上傳文件的服務(wù)器配置及案例2014-06-06
ios基于UICollectionView實現(xiàn)橫向瀑布流
這篇文章主要為大家詳細(xì)介紹了ios基于UICollectionView實現(xiàn)橫向瀑布流,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-12-12
iOS 隱藏導(dǎo)航條和狀態(tài)欄實現(xiàn)方法
這篇文章主要介紹了 iOS隱藏導(dǎo)航條和狀態(tài)欄實現(xiàn)方法的相關(guān)資料,有時候根據(jù)需求開發(fā)APP 需要隱藏導(dǎo)航欄和狀態(tài)欄,這里提供了實現(xiàn)方法需要的朋友可以參考下2016-11-11

