iOS自定义相机

2020-04-02  本文已影响0人  扶兮摇兮

本文是iOS自定义相机主要功能,包括前后置摄像头,闪光灯,检测环境光线明暗等功能

#import "SRPalmistryTakePhotoController.h"
#import <AVFoundation/AVFoundation.h>
#import "AppDelegate.h"

#define brightnessThresholdValue (-0.2)  //亮度阈值

@interface SRPalmistryTakePhotoController () <AVCapturePhotoCaptureDelegate,AVCaptureVideoDataOutputSampleBufferDelegate>

@property (nonatomic, strong) AVCaptureDevice *captureDevice;

@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;

@property (nonatomic, strong) AVCaptureSession *captureSession;

@property (nonatomic, strong) AVCaptureDeviceInput *deviceInput;

@property (nonatomic, strong) AVCaptureOutput *captureOutput;

@property (nonatomic, strong) AVCapturePhotoSettings *photoOutputSetting;
/// 闪光灯按钮
@property (nonatomic, strong) UIButton *flashButton;
///闪光提示文本
@property (nonatomic, strong) UILabel *flashPremptLabel;
///拍照按钮
@property (nonatomic, strong) UIButton *takePhotoButton;
/// 用这个videoDataOutPut来检测环境光线变化
@property (nonatomic, strong) AVCaptureVideoDataOutput *videoDataOutput;

@end

@implementation SRPalmistryTakePhotoController

- (void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (AVCaptureVideoDataOutput *)videoDataOutput{
    if (!_videoDataOutput) {
        _videoDataOutput = [[AVCaptureVideoDataOutput alloc] init];
        [_videoDataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
    }
    return _videoDataOutput;
}

- (UIButton *)flashButton {
    if (!_flashButton) {
        _flashButton = [[UIButton alloc] init];
        _flashButton.hidden = YES;
        [_flashButton addTarget:self action:@selector(flashButtonClick:) forControlEvents:UIControlEventTouchUpInside];
        [_flashButton setImage:[UIImage imageNamed:@"turnOn_flash"] forState:UIControlStateNormal];
        [_flashButton setImage:[UIImage imageNamed:@"btn_flash_off"] forState:UIControlStateSelected];
    }
    return _flashButton;
}

- (UILabel *)flashPremptLabel {
    if (!_flashPremptLabel) {
        _flashPremptLabel = [[UILabel alloc] init];
        _flashPremptLabel.textAlignment = NSTextAlignmentCenter;
        _flashPremptLabel.textColor = [UIColor colorWithHexString:@"#FFFFFF"];
        _flashPremptLabel.font = [UIFont SR_regularMerriweatherOfSize:SR_Ratio(10.0)];
        _flashPremptLabel.hidden = YES;
    }
    return _flashPremptLabel;
}

- (UIButton *)takePhotoButton {
    if (!_takePhotoButton) {
        _takePhotoButton = [[UIButton alloc] init];
        [_takePhotoButton addTarget:self action:@selector(takePhotoButtonClick:) forControlEvents:UIControlEventTouchUpInside];
    }
    return _takePhotoButton;
}
- (AVCaptureDevice *)captureDevice {

    if (!_captureDevice) {
        _captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        if ([[UIDevice currentDevice].systemVersion floatValue] < 10.0) {
            [_captureDevice lockForConfiguration:nil];
            [_captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];
            [_captureDevice unlockForConfiguration];
        }
    }
    return _captureDevice;
}

- (void)setIsLeftHand:(BOOL)isLeftHand{
    _isLeftHand = isLeftHand;
    if (isLeftHand) {
        [SRStatisticsManager palmistryCameraLeftOrRightHandSelectShowStatisticsWithKey1:[self getStatictisPosition] key2:@"left"];
    }else{
        [SRStatisticsManager palmistryCameraLeftOrRightHandSelectShowStatisticsWithKey1:[self getStatictisPosition] key2:@"right"];
    }
}

- (void)applicationDidEnterBackground{
    [self stopPlayVideoAndTextAnimation];
}
- (void)applicationWillEnterForeground{
    [self startPlayVideoAndTextAnimation];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
    
    self.navigationController.navigationBar.translucent = NO;
    UILabel *titleLabel = [[UILabel alloc] init];
    titleLabel.text = self.isLeftHand ? NSLocalizedStringFromTable(@"key_palm_takePhotoLeftTitle", @"handShapeMoudle", @"") : NSLocalizedStringFromTable(@"key_palm_takePhotoRightTitle", @"handShapeMoudle", @"");
    titleLabel.font = [UIFont SR_boldMerriweatherOfSize:SR_Ratio(18.0)];
    titleLabel.textColor = [UIColor colorWithHexString:@"#FFFFFF"];
    self.navigationItem.titleView = titleLabel;
    
    //导航栏右边按钮点击
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"palmistry_question"] style:UIBarButtonItemStylePlain target:self action:@selector(scanPalmistryGuidleAnimation)];
    
    self.captureDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    self.captureSession = [[AVCaptureSession alloc] init];
    self.captureSession.sessionPreset = AVCaptureSessionPreset640x480;
    NSError *error = nil;
    self.deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:self.captureDevice error:&error];

    CGFloat palmistryPhotoHeight = SR_Ratio(476.0); // 手相图片的高度
    CGFloat takePhotoButtonW = SR_Ratio(72.0); // 拍照按钮的宽高度
    CGFloat previewY = (SR_screenHeight - SR_statusAndNavigationHeight - SR_bottomSafeHeight - palmistryPhotoHeight - takePhotoButtonW - SR_Ratio(20.0))*0.5;;
    if (SR_screenWidth == 320) {
        previewY = SR_Ratio(5.0);
    }
    
    self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
    self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    self.previewLayer.frame = CGRectMake(0, previewY, SR_screenWidth,
                                         SR_screenWidth /0.75);
    [self.view.layer addSublayer:self.previewLayer];

    if ([self.captureSession canAddInput:self.deviceInput]) {
        [self.captureSession addInput:self.deviceInput];
    }

    if ([self.captureSession canAddOutput:self.captureOutput]) {
        [self.captureSession addOutput:self.captureOutput];
    }
    // 添加监测环境光线变化的输出
    if ([self.captureSession canAddOutput:self.videoDataOutput]) {
        [self.captureSession addOutput:self.videoDataOutput];
    }
    
    UIImageView *coverImageView = [[UIImageView alloc] init];
    [self.view addSubview:coverImageView];
    coverImageView.image = [self getCoverImaageWithWidth:SR_Ratio(256.0) height:palmistryPhotoHeight];
    coverImageView.frame = CGRectMake(0, 0, SR_screenWidth, SR_screenHeight - SR_statusAndNavigationHeight - SR_bottomSafeHeight);
    
    // 拍照按钮
    [self.view addSubview:self.takePhotoButton];
    [self.takePhotoButton setImage:[UIImage imageNamed:@"palmistry_takePhoto"] forState:UIControlStateNormal];
  
    CGFloat bottomMargin = SR_Ratio(41.0);
    CGFloat flashButtonBottomMargin = SR_Ratio(60.0);
    if (SR_screenWidth == 320) {
        bottomMargin = 10.0;
        flashButtonBottomMargin = 50;
    }
    
    CGFloat takePhotoButtonY = previewY + palmistryPhotoHeight + SR_Ratio(20);
    self.takePhotoButton.frame = CGRectMake((SR_screenWidth - takePhotoButtonW) * 0.5, takePhotoButtonY, takePhotoButtonW, takePhotoButtonW);
    if (SR_screenWidth == 320) {
    self.takePhotoButton.frame = CGRectMake((SR_screenWidth - takePhotoButtonW) * 0.5, SR_screenHeight - takePhotoButtonW - SR_statusAndNavigationHeight - SR_bottomSafeHeight - bottomMargin, takePhotoButtonW, takePhotoButtonW);
    }
    
    // 闪光灯按钮
    [self.view addSubview:self.flashButton];
    self.flashButton.frame = CGRectMake((SR_screenWidth - SR_Ratio(30))*0.5, self.takePhotoButton.frame.origin.y - SR_Ratio(30.0) - flashButtonBottomMargin , SR_Ratio(30.0), SR_Ratio(30.0));
       
    [self.view addSubview:self.flashPremptLabel];
    self.flashPremptLabel.text = NSLocalizedStringFromTable(@"key_palm_flashOnText", @"handShapeMoudle", @"");
    self.flashPremptLabel.frame = CGRectMake(0, CGRectGetMaxY(self.flashButton.frame) + SR_Ratio(6.0), SR_screenWidth, SR_Ratio(13));
}

#pragma mark -  AVCaptureVideoDataOutputSampleBufferDelegate - 
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
    
    CFDictionaryRef metadataDict = CMCopyDictionaryOfAttachments(NULL, sampleBuffer, kCMAttachmentMode_ShouldPropagate);
    NSDictionary *metadata = [[NSMutableDictionary alloc] initWithDictionary:(__bridge NSDictionary*)metadataDict];
    CFRelease(metadataDict);
    NSDictionary *exifMetadata = [[metadata objectForKey:(NSString *)kCGImagePropertyExifDictionary] mutableCopy];
    float brightnessValue = [[exifMetadata objectForKey:(NSString *)kCGImagePropertyExifBrightnessValue] floatValue];
    if (brightnessValue < brightnessThresholdValue) {
        dispatch_async(dispatch_get_main_queue(), ^{
            self.flashButton.hidden = NO;
            self.flashPremptLabel.hidden = NO;
        });
    }else{
        dispatch_async(dispatch_get_main_queue(), ^{
            self.flashButton.hidden = YES;
            self.flashPremptLabel.hidden = YES;
        });
    }
}
#pragma mark - 拍照 ---
- (void)takePhotoButtonClick:(UIButton *)button {

    [self cameraLimitCheckToAlert];
    
    AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if (authStatus !=AVAuthorizationStatusAuthorized) {
        return;
    }
    //拿到连接
    AVCaptureConnection *stillImageConnection = [self.captureOutput connectionWithMediaType:AVMediaTypeVideo];
    //拿到当前手机方向
    UIDeviceOrientation curDeviceOrientation = [[UIDevice currentDevice] orientation];
    //根据当前手机方向设置摄像头拍摄方向
    AVCaptureVideoOrientation avcaptureOrientation = [self avOrientationForDeviceOrientation:curDeviceOrientation];
    //设置图片方向
    [stillImageConnection setVideoOrientation:avcaptureOrientation];
    if ([[UIDevice currentDevice].systemVersion floatValue] < 10.0) {
        AVCaptureStillImageOutput *stillImageOutput = (AVCaptureStillImageOutput *)self.captureOutput;
        //生成静态图像数据
        [stillImageOutput captureStillImageAsynchronouslyFromConnection:stillImageConnection
                                                      completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {

                                                          if (imageDataSampleBuffer == NULL) {
                                                              return;
                                                          }
                                                          //拿到图片数据流
                                                          NSData *jpegData =
                                                              [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
                                                          //生成image图片(注意:这个image是没有时间/个人信息/地址信息的)
                                                          [self showPicture:[[UIImage alloc] initWithData:jpegData scale:[UIScreen mainScreen].scale]];
                                                      }];
    } else {
        /*
         这个地方有一个技巧:
     当前目前设备是后置摄像头/闪光灯开时,切换到前置时,那么flashmode要设置为闪光灯关闭模式,否则会崩溃,但是当再次从前置切换到后置时,闪光灯状态仍然要返回切换前后置之前的那个状态!这就是为何要专门要使用一个抢引用来引用self.photoSettings的原因
         */
        //!!!!使用这个方法一定要遵守规则哦!!!!
        // 1.重新创建配置(必须创建,一个setting只能使用一次前面说过)
        AVCapturePhotoSettings *newSettings = [AVCapturePhotoSettings photoSettingsFromPhotoSettings:self.photoOutputSetting];
        AVCapturePhotoOutput *photoOutput = (AVCapturePhotoOutput *)self.captureOutput;
        // 2.判断当前的闪光灯模式是否支持设备,如果不支持的话,会崩溃(闪光灯规则)
        //后置摄像头闪光灯支持0,1,2 //前置摄像头闪光灯只支持0
        if (![photoOutput.supportedFlashModes containsObject:@(newSettings.flashMode)]) {
            //不支持,那么当前设备一定是前置摄像头,那么修改配置
            newSettings.flashMode = AVCaptureFlashModeOff;
        }
        [photoOutput capturePhotoWithSettings:newSettings delegate:self];
    }
    
    [SRStatisticsManager palmistryCameraClickStatisticsWithKey1:[self getStatictisPosition] key2:@""];
}
#pragma mark - 拿到拍照图片 ---
- (void)showPicture:(UIImage *)image {
    // 暂停手相拍照
      if (self.captureSession) {
           [self.captureSession stopRunning];
      }
}

#pragma mark - AVCapturePhotoCaptureDelegate  用于监视照片的拍摄过程,实时获取到拍照的图层数据 --
- (void)captureOutput:(AVCapturePhotoOutput *)captureOutput
    didFinishProcessingPhotoSampleBuffer:(CMSampleBufferRef)photoSampleBuffer
                previewPhotoSampleBuffer:(CMSampleBufferRef)previewPhotoSampleBuffer
                        resolvedSettings:(AVCaptureResolvedPhotoSettings *)resolvedSettings
                         bracketSettings:(AVCaptureBracketedStillImageSettings *)bracketSettings
                                   error:(NSError *)error {
    if (error) {
        NSLog(@"error : %@", error.localizedDescription);
        return;
    }
    if (!photoSampleBuffer) {
        return;
    }
    NSData *data =
        [AVCapturePhotoOutput JPEGPhotoDataRepresentationForJPEGSampleBuffer:photoSampleBuffer previewPhotoSampleBuffer:previewPhotoSampleBuffer];
    [self showPicture:[[UIImage alloc] initWithData:data scale:[UIScreen mainScreen].scale]];
}

#pragma mark - 根据当前手机方向拿到摄像头拍摄方向 ---
- (AVCaptureVideoOrientation)avOrientationForDeviceOrientation:(UIDeviceOrientation)deviceOrientation {
    AVCaptureVideoOrientation result = (AVCaptureVideoOrientation)deviceOrientation;
    if (deviceOrientation == UIDeviceOrientationLandscapeLeft)
        result = AVCaptureVideoOrientationLandscapeRight;
    else if (deviceOrientation == UIDeviceOrientationLandscapeRight)
        result = AVCaptureVideoOrientationLandscapeLeft;
    return result;
}

- (UIImage *)getCoverImaageWithWidth:(CGFloat)imnageWidth height:(CGFloat)imageHeight{

    NSString *inImageName = @"hand_right";
    NSString *outImageName = @"hand_frame_right";
    
    if (self.isLeftHand) {
        inImageName = @"hand_left";
        outImageName = @"hand_frame_left";
    }
    
    UIImage *innerImage = [UIImage imageNamed:inImageName];
    UIImage *outerImage = [UIImage imageNamed:outImageName];

    CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
    shapeLayer.backgroundColor = [UIColor blackColor].CGColor;

    CGFloat contextHeight = SR_screenHeight - SR_statusAndNavigationHeight - SR_bottomSafeHeight;
    
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(SR_screenWidth, contextHeight), NO, [UIScreen mainScreen].scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [UIColor colorWithHexString:@"#10101D"].CGColor);
    CGContextFillRect(context, CGRectMake(0, 0, SR_screenWidth, contextHeight));

    CGFloat imageX = (SR_screenWidth - imnageWidth) * 0.5;
    CGFloat imageY = (contextHeight - imageHeight - SR_Ratio(92)) * 0.5;
    if (SR_screenWidth == 320) {
       imageY = SR_Ratio(5.0) + (SR_screenWidth/0.75 - imageHeight)*0.5;
    }
    [outerImage drawInRect:CGRectMake(imageX, imageY, imnageWidth, imageHeight)];
    [innerImage drawInRect:CGRectMake(imageX, imageY, imnageWidth, imageHeight) blendMode:kCGBlendModeDestinationOut alpha:1];

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

#pragma mark - 初始化AVCaptureOutput --
- (AVCaptureOutput *)captureOutput {
    if (_captureOutput == nil) {
        // 10.0之前的创建方式
        if ([[UIDevice currentDevice].systemVersion floatValue] < 10.0) {
            AVCaptureStillImageOutput *output = [[AVCaptureStillImageOutput alloc] init];
            //对输出进行配置支持哪些格式
            NSDictionary *outputSettings = [[NSDictionary alloc] initWithObjectsAndKeys:AVVideoCodecJPEG, AVVideoCodecKey, nil];
            [output setOutputSettings:outputSettings];
            _captureOutput = output;
        } else {
            // 10.0之后的创建方式
            //创建输出
            AVCapturePhotoOutput *photoOutput = [[AVCapturePhotoOutput alloc] init];
            //配置输出
            AVCapturePhotoSettings *photoOutputSet = [AVCapturePhotoSettings photoSettings];
            self.photoOutputSetting = photoOutputSet;
            // [photoOutput capturePhotoWithSettings:photoOutputSet delegate:self];
            //初始化闪光灯设置
            photoOutputSet.flashMode = AVCaptureFlashModeAuto;
            [photoOutput setPhotoSettingsForSceneMonitoring:photoOutputSet];
            _captureOutput = photoOutput;
        }
    }
    return _captureOutput;
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    [self.navigationController setNavigationBarHidden:NO animated:NO];
    if (self.captureSession) {
        [self.captureSession startRunning];
    }
    [SRStatisticsManager palmistryCameraShowShowStatisticsWithKey1:[self getStatictisPosition] key2:@""];
    [self startPlayVideoAndTextAnimation];
    
}
#pragma mark - 开始播放视屏动画 --
- (void)startPlayVideoAndTextAnimation{
    
    if (self.isExistAnimation) {
        [self scanPalmistryGuidleAnimation];
    }
}
- (NSString *)getStatictisPosition{
    
    NSString *position = @"Set_up";
   SRTakePhotoJumpMrakType type = [[SRStoreManager shareManager] getTakePhotoJumpMark];
    if (type == SRTakePhotoJumpMrakTypePalmistryError) {
        position = @"palm";
    }
    
    if (type == SRTakePhotoJumpMrakTypeNone) {
        position = @"information";
    }
    
    return position;
}

- (void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    
    [self cameraLimitCheckToAlert];
}

- (void)cameraLimitCheckToAlert{
    
    AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    if (authStatus == AVAuthorizationStatusNotDetermined) {
        // 申请权限
        [SRStatisticsManager permissionsS];
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
            dispatch_async(dispatch_get_main_queue(), ^{
                if (granted) {
                    self.flashButton.hidden = NO;
                    self.flashPremptLabel.hidden = NO;
                    [SRStatisticsManager permissionsC:@"OK"];
                }else{
                    [self showPermissionTipsAlert];
                    [SRStatisticsManager permissionsC:@"cancel"];
                }
            });
        }];
        return;
    }
     if (authStatus == AVAuthorizationStatusRestricted || authStatus ==AVAuthorizationStatusDenied) {
        //无权限 可以做一个友好的提示
         [self showPermissionTipsAlert];
         return;
     }
    if (authStatus == AVAuthorizationStatusAuthorized) {
        // 权限已获取
        self.flashButton.hidden = NO;
        self.flashPremptLabel.hidden = NO;
    }
}

- (void)showPermissionTipsAlert{
    [SRStatisticsManager setCameraPermissionsS];
    UIAlertController *alertView = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"key_palm_toJumpSettingAlertTitle", @"handShapeMoudle", @"") message:@"" preferredStyle:UIAlertControllerStyleAlert];
             UIAlertAction *action = [UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"key_palm_settingAction", @"handShapeMoudle", @"") style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
    //             [self.navigationController popViewControllerAnimated:YES];
                 [self go2PermissionSetting];
                 [SRStatisticsManager setCameraPermissionsC];
             }];
    [alertView addAction:action];
    [self presentViewController:alertView animated:YES completion:nil];
    self.flashButton.hidden = YES;
    self.flashPremptLabel.hidden = YES;
}

- (void)go2PermissionSetting{
    NSURL * url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
    if([[UIApplication sharedApplication] canOpenURL:url]) {
        NSURL*url =[NSURL URLWithString:UIApplicationOpenSettingsURLString];
        [[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
    }
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
     [self.navigationController setNavigationBarHidden:YES animated:NO];

    if (self.captureSession) {
        [self.captureSession stopRunning];
    }
    [self stopPlayVideoAndTextAnimation];
}

#pragma mark - 闪光灯模式切换 --
- (void)flashButtonClick:(UIButton *)sender {
    //拿到后置摄像头
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
    //自动->打开->关闭
    //必须判定是否有闪光灯,否则如果没有闪光灯会崩溃
    if (!device.hasFlash) {
        NSLog(@"设备不支持闪光灯");
        return;
    }
    sender.selected = !sender.selected;
    AVCaptureFlashMode tempflashMode = sender.selected ? AVCaptureFlashModeOn : AVCaptureFlashModeOff;
    self.flashPremptLabel.text = sender.selected ? NSLocalizedStringFromTable(@"key_palm_flashOffText", @"handShapeMoudle", @"") : NSLocalizedStringFromTable(@"key_palm_flashOnText", @"handShapeMoudle", @"");
    if ([[UIDevice currentDevice].systemVersion floatValue] < 10.0) {
        //修改前必须先锁定
        [device lockForConfiguration:nil];
        if ([device isFlashModeSupported:tempflashMode])
            [device setFlashMode:tempflashMode];
        [device unlockForConfiguration];
    } else {
        //修改outputseting的闪光灯模式
        self.photoOutputSetting.flashMode = tempflashMode;
    }
}
@end

上一篇下一篇

猜你喜欢

热点阅读