iOS簡單自定義相機
阿新 • • 發佈:2019-01-29
最近在做一個趣味相機的小專案,分享一下自己在學習的過程中的收穫。
首先,我們需要知道的是,自定義相機也是和系統相機一樣,需要呼叫手機的硬體,所以相機的工作流程大致為:
1、獲取裝置
2、輸入裝置
3、輸出資料
4、在預覽層展示資料
所以我們需要先匯入框架
#import<AVFoundation/AVFoundation.h>
然後宣告幾個必要的物件
@property (nonatomic, strong) AVCaptureDevice *device; //獲取裝置
@property (nonatomic, strong ) AVCaptureDeviceInput* videoInput; //輸入裝置
@property (nonatomic, strong) AVCaptureSession* session; //AVCaptureSession物件來執行輸入裝置和輸出裝置之間的資料傳遞
@property (nonatomic, strong) AVCaptureStillImageOutput* stillImageOutput; //輸出資料
@property (nonatomic, strong) AVCaptureVideoPreviewLayer* previewLayer; //預覽層顯示影象
@property (nonatomic, strong) UIImageView *previewImageView;//顯示拍下的照片
@property (nonatomic, strong) UIImage *previewImage;
初始化
- (void)viewDidLoad{
[super viewDidLoad];
//這裡可以就在viewDidload中初始化了
self.device = [self cameraWithPosition:AVCaptureDevicePositionFront];
//更改這個設定的時候必須先鎖定裝置,修改完後再解鎖,否則崩潰
[device lockForConfiguration:nil ];
//設定閃光燈為自動
[device unlockForConfiguration];
self.videoInput = [[AVCaptureDeviceInput alloc] initWithDevice:self.device error:nil];//此處也可以做一個錯誤判斷
self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
//輸出設定。AVVedioCodecJPEG 輸出jpeg格式圖片
NSDictionary *outPutSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG,AVVideoCodecKey, nil];
[self.stillImageOutput setOutputSettings:outPutSettings];
self.session = [[AVCaptureSession alloc] init];
self.session.sessionPreset = AVCaptureSessionPreset640x480;
// 拿到的影象的大小可以自行設定
// AVCaptureSessionPreset320x240
// AVCaptureSessionPreset352x288
// AVCaptureSessionPreset640x480
// AVCaptureSessionPreset960x540
// AVCaptureSessionPreset1280x720
// AVCaptureSessionPreset1920x1080
// AVCaptureSessionPreset3840x2160
// 預設為全屏
//輸入輸出裝置結合
if ([self.session canAddInput:self.videoInput]) {
[self.session addInput:self.videoInput];
}
if ([self.session canAddOutput:self.stillImageOutput]) {
[self.session addOutput:self.stillImageOutput];
}
//初始化預覽層
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
[self.previewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
self.previewLayer.frame = CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
self.view.layer.masksToBounds = YES;
[self.view.layer addSublayer:self.previewLayer];
}
之後在viewWillAppear,viewDidDisappear方法裡開啟和關閉session
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:YES];
if (self.session) {
[self.session startRunning];
}
}
- (void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:YES];
if (self.session) {
[self.session stopRunning];
}
}
至此初始化工作基本完成,已經可以在程式中顯示出裝置捕捉到的畫面,接下來需要一個獲取裝置方向的方法,再配置圖片輸出的時候需要使用
- (AVCaptureDevice *)cameraWithPosition:(AVCaptureDevicePosition)position{
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for ( AVCaptureDevice *device in devices )
if ( device.position == position ){
return device;
}
return nil;
}
拍照按鈕方法
- (void)action_TakeShot:(UIButton *)sender{
NSLog(@"------------- 拍照");
AVCaptureConnection *connection = [self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
if (!connection) {
NSLog(@"拍照失敗!");
return;
}
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if (imageDataSampleBuffer == nil) {
return ;
}
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
self.previewImage = [UIImage imageWithData:imageData];
[self.session stopRunning];
[self.view addSubview:self.previewImageView];
}];
}
到此相機的基本功能就已實現,再此基礎上再新增儲存照片、閃光燈、前後置攝像頭切換、觸屏對焦等功能
閃光燈
- (void)flashButtonClick:(UIButton *)sender {
NSLog(@"------------- 閃光燈");
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
//修改前必須先鎖定
[device lockForConfiguration:nil];
//必須判定是否有閃光燈,否則如果沒有閃光燈會崩潰
if ([device hasFlash]) {
if (device.flashMode == AVCaptureFlashModeOff) {
device.flashMode = AVCaptureFlashModeOn;
[sender setTitle:@"flashOn"];
} else if (device.flashMode == AVCaptureFlashModeOn) {
device.flashMode = AVCaptureFlashModeAuto;
[sender setTitle:@"flashAuto"];
} else if (device.flashMode == AVCaptureFlashModeAuto) {
device.flashMode = AVCaptureFlashModeOff;
[sender setTitle:@"flashOff"];
}
} else {
NSLog(@"裝置不支援閃光燈");
}
[device unlockForConfiguration];
}
device; //獲取裝置
videoInput; //輸入裝置
session; //AVCaptureSession物件來執行輸入裝置和輸出裝置之間的資料傳遞
stillImageOutput; //輸出資料
previewLayer; //預覽層顯示影象
previewImageView;//顯示拍下的照片
previewImage;
切換前後置攝像頭
- (void)changeCamera:(UIButton *)sender{
NSUInteger cameraCount = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count];
if (cameraCount > 1) {
NSError *error;
//給攝像頭的切換新增翻轉動畫
CATransition *animation = [CATransition animation];
animation.duration = .5f;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.type = @"oglFlip";
AVCaptureDevice *newCamera = nil;
AVCaptureDeviceInput *newInput = nil;
//拿到另外一個攝像頭位置
AVCaptureDevicePosition position = [[_input device] position];
if (position == AVCaptureDevicePositionFront){
newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
animation.subtype = kCATransitionFromLeft;//動畫翻轉方向
}
else {
newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
animation.subtype = kCATransitionFromRight;//動畫翻轉方向
}
//生成新的輸入
newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];
[self.previewLayer addAnimation:animation forKey:nil];
if (newInput != nil) {
[self.session beginConfiguration];
[self.session removeInput:self.input];
if ([self.session canAddInput:newInput]) {
[self.session addInput:newInput];
self.input = newInput;
} else {
[self.session addInput:self.input];
}
[self.session commitConfiguration];
} else if (error) {
NSLog(@"toggle carema failed, error = %@", error);
}
}
}