iOS使用視聽媒體框架AVFoundation實(shí)現(xiàn)照片拍攝
用系統(tǒng)自帶的視聽媒體的框架,AVFoundation實(shí)現(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 { //初始化會(huì)話 _captureSession = [[AVCaptureSession alloc] init]; //設(shè)置分辨率 if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) { _captureSession.sessionPreset = AVCaptureSessionPreset1280x720; } //獲得輸入設(shè)備,取得后置攝像頭 AVCaptureDevice *captureDevice = [self getCameraDeviceWithPosition:AVCaptureDevicePositionBack]; if (!captureDevice) { NSLog(@"取得后置攝像頭時(shí)出現(xiàn)問(wèn)題"); return; } NSError *error = nil; //根據(jù)輸入設(shè)備初始化設(shè)備輸入對(duì)象,用于獲得輸入數(shù)據(jù) _captureDeviceInput = [[AVCaptureDeviceInput alloc]initWithDevice:captureDevice error:&error]; if (error) { NSLog(@"取得設(shè)備輸入對(duì)象時(shí)出錯(cuò),錯(cuò)誤原因:%@", error.localizedDescription); return; } //初始化設(shè)備輸出對(duì)象,用于獲得輸出數(shù)據(jù) _captureStillImageOutput = [[AVCaptureStillImageOutput alloc] init]; NSDictionary *outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG}; //輸出設(shè)置 [_captureStillImageOutput setOutputSettings:outputSettings]; //將設(shè)備輸入添加到會(huì)話中 if ([_captureSession canAddInput:_captureDeviceInput]) { [_captureSession addInput:_captureDeviceInput]; } //將設(shè)備輸出添加到會(huì)話中 if ([_captureSession canAddOutput:_captureStillImageOutput]) { [_captureSession addOutput:_captureStillImageOutput]; } //創(chuàng)建視頻預(yù)覽層,用于實(shí)時(shí)展示攝像頭狀態(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è)備輸入對(duì)象 AVCaptureDeviceInput *toChangeDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:toChangeDevice error:nil]; //改變會(huì)話的配置前一定要先開啟配置,配置完成后提交配置改變 [self.captureSession beginConfiguration]; //移除原有輸入對(duì)象 [self.captureSession removeInput:self.captureDeviceInput]; //添加新的輸入對(duì)象 if ([self.captureSession canAddInput:toChangeDeviceInput]) { [self.captureSession addInput:toChangeDeviceInput]; self.captureDeviceInput = toChangeDeviceInput; } //提交會(huì)話配置 [self.captureSession commitConfiguration]; } //改變?cè)O(shè)備屬性的統(tǒng)一操作方法 - (void)changeDeviceProperty:(void (^)(AVCaptureDevice *))propertyChange { AVCaptureDevice *captureDevice = [self.captureDeviceInput device]; NSError *error; //注意改變?cè)O(shè)備屬性前一定要首先調(diào)用lockForConfiguration:調(diào)用完之后使用unlockForConfiguration方法解鎖 if ([captureDevice lockForConfiguration:&error]) { propertyChange(captureDevice); [captureDevice unlockForConfiguration]; }else { NSLog(@"設(shè)置設(shè)備屬性過(guò)程發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@", 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è)置聚焦點(diǎn) - (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]; } }]; } //添加點(diǎn)按手勢(shì),點(diǎn)按時(shí)聚焦 - (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下載鏈接
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
加載帶有手勢(shì)識(shí)別器的XIB文件需注意哪些問(wèn)題
手勢(shì)識(shí)別在iOS上非常重要,手勢(shì)操作移動(dòng)設(shè)備的重要特征,極大的增加了移動(dòng)設(shè)備使用便捷性。通過(guò)本教程給大家介紹加載帶有手勢(shì)識(shí)別器的XIB文件需注意哪些問(wèn)題,需要的朋友可以參考下2015-08-08iOS實(shí)現(xiàn)步驟進(jìn)度條功能實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于iOS實(shí)現(xiàn)步驟進(jìn)度條功能的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11IOS開發(fā)教程之put上傳文件的服務(wù)器的配置及實(shí)例分享
IOS開發(fā)給人一種高大上的趕腳,弄的好像你不會(huì)IOS就不是個(gè)合格的程序員一樣,好吧,既然是大趨勢(shì),那我們就來(lái)學(xué)習(xí)下吧,今天來(lái)看看put上傳文件的服務(wù)器配置及案例2014-06-06ios基于UICollectionView實(shí)現(xiàn)橫向瀑布流
這篇文章主要為大家詳細(xì)介紹了ios基于UICollectionView實(shí)現(xiàn)橫向瀑布流,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12iOS 隱藏導(dǎo)航條和狀態(tài)欄實(shí)現(xiàn)方法
這篇文章主要介紹了 iOS隱藏導(dǎo)航條和狀態(tài)欄實(shí)現(xiàn)方法的相關(guān)資料,有時(shí)候根據(jù)需求開發(fā)APP 需要隱藏導(dǎo)航欄和狀態(tài)欄,這里提供了實(shí)現(xiàn)方法需要的朋友可以參考下2016-11-11iOS多控制器實(shí)現(xiàn)帶滑動(dòng)動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了iOS多控制器實(shí)現(xiàn)帶滑動(dòng)動(dòng)畫,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06