本文是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
网友评论