1. 程式人生 > >iOS簡單自定義相機

iOS簡單自定義相機

最近在做一個趣味相機的小專案,分享一下自己在學習的過程中的收穫。

首先,我們需要知道的是,自定義相機也是和系統相機一樣,需要呼叫手機的硬體,所以相機的工作流程大致為:
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);
        }
    }
}