You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
489 lines
17 KiB
489 lines
17 KiB
1 year ago
|
//
|
||
|
// CACameraSessionDelegate.h
|
||
|
//
|
||
|
// Created by Christopher Cohen & Gabriel Alvarado on 1/23/15.
|
||
|
// Copyright (c) 2015 Gabriel Alvarado. All rights reserved.
|
||
|
//
|
||
|
|
||
|
#import "CameraSessionView.h"
|
||
|
#import "CaptureSessionManager.h"
|
||
|
#import <ImageIO/ImageIO.h>
|
||
|
|
||
|
//Custom UI classes
|
||
|
#import "CameraShutterButton.h"
|
||
|
#import "CameraToggleButton.h"
|
||
|
#import "CameraFlashButton.h"
|
||
|
#import "CameraDismissButton.h"
|
||
|
#import "CameraFocalReticule.h"
|
||
|
#import "Constants.h"
|
||
|
|
||
|
@interface CameraSessionView () <CaptureSessionManagerDelegate>
|
||
|
{
|
||
|
//Size of the UI elements variables
|
||
|
CGSize shutterButtonSize;
|
||
|
CGSize topBarSize;
|
||
|
CGSize barButtonItemSize;
|
||
|
|
||
|
//Variable vith the current camera being used (Rear/Front)
|
||
|
CameraType cameraBeingUsed;
|
||
|
}
|
||
|
|
||
|
//Primative Properties
|
||
|
@property (readwrite) BOOL animationInProgress;
|
||
|
|
||
|
//Object References
|
||
|
@property (nonatomic, strong) CaptureSessionManager *captureManager;
|
||
|
@property (nonatomic, strong) CameraShutterButton *cameraShutter;
|
||
|
@property (nonatomic, strong) CameraToggleButton *cameraToggle;
|
||
|
@property (nonatomic, strong) CameraFlashButton *cameraFlash;
|
||
|
@property (nonatomic, strong) CameraDismissButton *cameraDismiss;
|
||
|
@property (nonatomic, strong) CameraFocalReticule *focalReticule;
|
||
|
@property (nonatomic, strong) UIView *topBarView;
|
||
|
|
||
|
//Temporary/Diagnostic properties
|
||
|
@property (nonatomic, strong) UILabel *ISOLabel, *apertureLabel, *shutterSpeedLabel;
|
||
|
|
||
|
@end
|
||
|
|
||
|
@implementation CameraSessionView
|
||
|
|
||
|
-(void)drawRect:(CGRect)rect {
|
||
|
if (self) {
|
||
|
_animationInProgress = NO;
|
||
|
[self setupCaptureManager:RearFacingCamera];
|
||
|
cameraBeingUsed = RearFacingCamera;
|
||
|
[self composeInterface];
|
||
|
|
||
|
[[_captureManager captureSession] startRunning];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
-(instancetype)initWithFrame:(CGRect)frame {
|
||
|
self = [super initWithFrame:frame];
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
- (id)initWithCoder:(NSCoder *)aDecoder {
|
||
|
self = [super initWithCoder:aDecoder];
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
#pragma mark - Setup
|
||
|
|
||
|
-(void)setupCaptureManager:(CameraType)camera {
|
||
|
|
||
|
// remove existing input
|
||
|
AVCaptureInput* currentCameraInput = [self.captureManager.captureSession.inputs objectAtIndex:0];
|
||
|
[self.captureManager.captureSession removeInput:currentCameraInput];
|
||
|
|
||
|
_captureManager = nil;
|
||
|
|
||
|
//Create and configure 'CaptureSessionManager' object
|
||
|
_captureManager = [CaptureSessionManager new];
|
||
|
|
||
|
// indicate that some changes will be made to the session
|
||
|
[self.captureManager.captureSession beginConfiguration];
|
||
|
|
||
|
if (_captureManager) {
|
||
|
|
||
|
//Configure
|
||
|
[_captureManager setDelegate:self];
|
||
|
[_captureManager initiateCaptureSessionForCamera:camera];
|
||
|
[_captureManager addStillImageOutput];
|
||
|
[_captureManager addVideoPreviewLayer];
|
||
|
[self.captureManager.captureSession commitConfiguration];
|
||
|
|
||
|
//Preview Layer setup
|
||
|
CGRect layerRect = self.layer.bounds;
|
||
|
[_captureManager.previewLayer setBounds:layerRect];
|
||
|
[_captureManager.previewLayer setPosition:CGPointMake(CGRectGetMidX(layerRect),CGRectGetMidY(layerRect))];
|
||
|
|
||
|
//Apply animation effect to the camera's preview layer
|
||
|
CATransition *applicationLoadViewIn =[CATransition animation];
|
||
|
[applicationLoadViewIn setDuration:0.6];
|
||
|
[applicationLoadViewIn setType:kCATransitionReveal];
|
||
|
[applicationLoadViewIn setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
|
||
|
[_captureManager.previewLayer addAnimation:applicationLoadViewIn forKey:kCATransitionReveal];
|
||
|
|
||
|
//Add to self.view's layer
|
||
|
[self.layer addSublayer:_captureManager.previewLayer];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
-(void)composeInterface {
|
||
|
|
||
|
//Adding notifier for orientation changes
|
||
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
|
||
|
|
||
|
|
||
|
//Define adaptable sizing variables for UI elements to the right device family (iPhone or iPad)
|
||
|
if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad )
|
||
|
{
|
||
|
//Declare the sizing of the UI elements for iPad
|
||
|
shutterButtonSize = CGSizeMake(self.bounds.size.width * 0.1, self.bounds.size.width * 0.1);
|
||
|
topBarSize = CGSizeMake(self.frame.size.width, self.frame.size.height * 0.06);
|
||
|
barButtonItemSize = CGSizeMake([[UIScreen mainScreen] bounds].size.height * 0.04, [[UIScreen mainScreen] bounds].size.height * 0.04);
|
||
|
} else
|
||
|
{
|
||
|
//Declare the sizing of the UI elements for iPhone
|
||
|
shutterButtonSize = CGSizeMake(self.bounds.size.width * 0.21, self.bounds.size.width * 0.21);
|
||
|
topBarSize = CGSizeMake(self.frame.size.width, [[UIScreen mainScreen] bounds].size.height * 0.07);
|
||
|
barButtonItemSize = CGSizeMake([[UIScreen mainScreen] bounds].size.height * 0.05, [[UIScreen mainScreen] bounds].size.height * 0.05);
|
||
|
}
|
||
|
|
||
|
|
||
|
//Create shutter button
|
||
|
_cameraShutter = [CameraShutterButton new];
|
||
|
|
||
|
if (_captureManager && !_hiddenShutter) {
|
||
|
|
||
|
//Button Visual attribution
|
||
|
_cameraShutter.frame = (CGRect){0,0, shutterButtonSize};
|
||
|
_cameraShutter.center = CGPointMake(self.frame.size.width/2, self.frame.size.height*0.875);
|
||
|
_cameraShutter.tag = ShutterButtonTag;
|
||
|
_cameraShutter.backgroundColor = [UIColor clearColor];
|
||
|
|
||
|
//Button target
|
||
|
[_cameraShutter addTarget:self action:@selector(inputManager:) forControlEvents:UIControlEventTouchUpInside];
|
||
|
[self addSubview:_cameraShutter];
|
||
|
}
|
||
|
|
||
|
//Create the top bar and add the buttons to it
|
||
|
_topBarView = [UIView new];
|
||
|
|
||
|
if (_topBarView && !_hiddenTopBar) {
|
||
|
|
||
|
//Setup visual attribution for bar
|
||
|
_topBarView.frame = (CGRect){0,0, topBarSize};
|
||
|
_topBarView.backgroundColor = [UIColor colorWithRed: 0.176 green: 0.478 blue: 0.529 alpha: 0.64];
|
||
|
[self addSubview:_topBarView];
|
||
|
|
||
|
//Add the flash button
|
||
|
_cameraFlash = [CameraFlashButton new];
|
||
|
if (_cameraFlash) {
|
||
|
_cameraFlash.frame = (CGRect){0,0, barButtonItemSize};
|
||
|
_cameraFlash.center = CGPointMake(_topBarView.center.x * 0.80, _topBarView.center.y);
|
||
|
_cameraFlash.tag = FlashButtonTag;
|
||
|
if ( UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPad ) [_topBarView addSubview:_cameraFlash];
|
||
|
}
|
||
|
|
||
|
//Add the camera toggle button
|
||
|
_cameraToggle = [CameraToggleButton new];
|
||
|
if (_cameraToggle) {
|
||
|
_cameraToggle.frame = (CGRect){0,0, barButtonItemSize};
|
||
|
_cameraToggle.center = CGPointMake(_topBarView.center.x * 1.20, _topBarView.center.y);
|
||
|
_cameraToggle.tag = ToggleButtonTag;
|
||
|
[_topBarView addSubview:_cameraToggle];
|
||
|
}
|
||
|
|
||
|
//Add the camera dismiss button
|
||
|
_cameraDismiss = [CameraDismissButton new];
|
||
|
if (_cameraDismiss) {
|
||
|
_cameraDismiss.frame = (CGRect){0,0, barButtonItemSize};
|
||
|
_cameraDismiss.center = CGPointMake(20, _topBarView.center.y);
|
||
|
_cameraDismiss.tag = DismissButtonTag;
|
||
|
[_topBarView addSubview:_cameraDismiss];
|
||
|
}
|
||
|
|
||
|
//Attribute and configure all buttons in the bar's subview
|
||
|
for (UIButton *button in _topBarView.subviews) {
|
||
|
button.backgroundColor = [UIColor clearColor];
|
||
|
[button addTarget:self action:@selector(inputManager:) forControlEvents:UIControlEventTouchUpInside];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Create the focus reticule UIView
|
||
|
_focalReticule = [CameraFocalReticule new];
|
||
|
|
||
|
if (_focalReticule) {
|
||
|
|
||
|
_focalReticule.frame = (CGRect){0,0, 60, 60};
|
||
|
_focalReticule.backgroundColor = [UIColor clearColor];
|
||
|
_focalReticule.hidden = YES;
|
||
|
[self addSubview:_focalReticule];
|
||
|
}
|
||
|
|
||
|
//Create the gesture recognizer for the focus tap
|
||
|
UITapGestureRecognizer *singleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(focusGesture:)];
|
||
|
if (singleTapGestureRecognizer) [self addGestureRecognizer:singleTapGestureRecognizer];
|
||
|
|
||
|
}
|
||
|
|
||
|
#pragma mark - User Interaction
|
||
|
|
||
|
-(void)inputManager:(id)sender {
|
||
|
|
||
|
//If animation is in progress, ignore input
|
||
|
if (_animationInProgress) return;
|
||
|
|
||
|
//If sender does not inherit from 'UIButton', return
|
||
|
if (![sender isKindOfClass:[UIButton class]]) return;
|
||
|
|
||
|
//Input manager switch
|
||
|
switch ([(UIButton *)sender tag]) {
|
||
|
case ShutterButtonTag: [self onTapShutterButton]; return;
|
||
|
case ToggleButtonTag: [self onTapToggleButton]; return;
|
||
|
case FlashButtonTag: [self onTapFlashButton]; return;
|
||
|
case DismissButtonTag: [self onTapDismissButton]; return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (void)onTapShutterButton {
|
||
|
|
||
|
//Animate shutter release
|
||
|
[self animateShutterRelease];
|
||
|
|
||
|
//Capture image from camera
|
||
|
[_captureManager captureStillImage];
|
||
|
}
|
||
|
|
||
|
- (void)onTapFlashButton {
|
||
|
BOOL enable = !self.captureManager.isTorchEnabled;
|
||
|
self.captureManager.enableTorch = enable;
|
||
|
}
|
||
|
|
||
|
- (void)onTapToggleButton {
|
||
|
if (cameraBeingUsed == RearFacingCamera) {
|
||
|
[self setupCaptureManager:FrontFacingCamera];
|
||
|
cameraBeingUsed = FrontFacingCamera;
|
||
|
[self composeInterface];
|
||
|
[[_captureManager captureSession] startRunning];
|
||
|
_cameraFlash.hidden = YES;
|
||
|
} else {
|
||
|
[self setupCaptureManager:RearFacingCamera];
|
||
|
cameraBeingUsed = RearFacingCamera;
|
||
|
[self composeInterface];
|
||
|
[[_captureManager captureSession] startRunning];
|
||
|
_cameraFlash.hidden = NO;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (void)onTapDismissButton {
|
||
|
[_captureManager stop];
|
||
|
if ([self.delegate respondsToSelector:@selector(didDismiss)]) {
|
||
|
[self.delegate didDismiss];
|
||
|
}
|
||
|
// [UIView animateWithDuration:0.3 animations:^{
|
||
|
// self.center = CGPointMake(self.center.x, self.center.y*3);
|
||
|
// } completion:^(BOOL finished) {
|
||
|
// [self removeFromSuperview];
|
||
|
//
|
||
|
// }];
|
||
|
}
|
||
|
|
||
|
- (void)focusGesture:(id)sender {
|
||
|
|
||
|
if ([sender isKindOfClass:[UITapGestureRecognizer class]]) {
|
||
|
UITapGestureRecognizer *tap = sender;
|
||
|
if (tap.state == UIGestureRecognizerStateRecognized) {
|
||
|
CGPoint location = [sender locationInView:self];
|
||
|
|
||
|
[self focusAtPoint:location completionHandler:^{
|
||
|
[self animateFocusReticuleToPoint:location];
|
||
|
}];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#pragma mark - Animation
|
||
|
|
||
|
- (void)animateShutterRelease {
|
||
|
|
||
|
_animationInProgress = YES; //Disables input manager
|
||
|
|
||
|
[UIView animateWithDuration:.1 animations:^{
|
||
|
_cameraShutter.transform = CGAffineTransformMakeScale(1.25, 1.25);
|
||
|
} completion:^(BOOL finished) {
|
||
|
[UIView animateWithDuration:.1 animations:^{
|
||
|
_cameraShutter.transform = CGAffineTransformMakeScale(1, 1);
|
||
|
} completion:^(BOOL finished) {
|
||
|
|
||
|
_animationInProgress = NO; //Enables input manager
|
||
|
}];
|
||
|
}];
|
||
|
}
|
||
|
|
||
|
- (void)animateFocusReticuleToPoint:(CGPoint)targetPoint
|
||
|
{
|
||
|
_animationInProgress = YES; //Disables input manager
|
||
|
|
||
|
[self.focalReticule setCenter:targetPoint];
|
||
|
self.focalReticule.alpha = 0.0;
|
||
|
self.focalReticule.hidden = NO;
|
||
|
|
||
|
[UIView animateWithDuration:0.4 animations:^{
|
||
|
self.focalReticule.alpha = 1.0;
|
||
|
} completion:^(BOOL finished) {
|
||
|
[UIView animateWithDuration:0.4 animations:^{
|
||
|
self.focalReticule.alpha = 0.0;
|
||
|
}completion:^(BOOL finished) {
|
||
|
|
||
|
_animationInProgress = NO; //Enables input manager
|
||
|
}];
|
||
|
}];
|
||
|
}
|
||
|
|
||
|
- (void)orientationChanged:(NSNotification *)notification{
|
||
|
|
||
|
//Animate top bar buttons on orientation changes
|
||
|
switch ([[UIDevice currentDevice] orientation]) {
|
||
|
case UIDeviceOrientationPortrait:
|
||
|
{
|
||
|
//Standard device orientation (Portrait)
|
||
|
[UIView animateWithDuration:0.6 animations:^{
|
||
|
CGAffineTransform transform = CGAffineTransformMakeRotation( 0 );
|
||
|
|
||
|
_cameraFlash.transform = transform;
|
||
|
_cameraFlash.center = CGPointMake(_topBarView.center.x * 0.80, _topBarView.center.y);
|
||
|
|
||
|
_cameraToggle.transform = transform;
|
||
|
_cameraToggle.center = CGPointMake(_topBarView.center.x * 1.20, _topBarView.center.y);
|
||
|
|
||
|
_cameraDismiss.center = CGPointMake(20, _topBarView.center.y);
|
||
|
}];
|
||
|
}
|
||
|
break;
|
||
|
case UIDeviceOrientationLandscapeLeft:
|
||
|
{
|
||
|
//Device orientation changed to landscape left
|
||
|
[UIView animateWithDuration:0.6 animations:^{
|
||
|
CGAffineTransform transform = CGAffineTransformMakeRotation( M_PI_2 );
|
||
|
|
||
|
_cameraFlash.transform = transform;
|
||
|
_cameraFlash.center = CGPointMake(_topBarView.center.x * 1.25, _topBarView.center.y);
|
||
|
|
||
|
_cameraToggle.transform = transform;
|
||
|
_cameraToggle.center = CGPointMake(_topBarView.center.x * 1.60, _topBarView.center.y);
|
||
|
|
||
|
_cameraDismiss.center = CGPointMake(_topBarView.center.x * 0.25, _topBarView.center.y);
|
||
|
}];
|
||
|
}
|
||
|
break;
|
||
|
case UIDeviceOrientationLandscapeRight:
|
||
|
{
|
||
|
//Device orientation changed to landscape right
|
||
|
[UIView animateWithDuration:0.6 animations:^{
|
||
|
CGAffineTransform transform = CGAffineTransformMakeRotation( - M_PI_2 );
|
||
|
|
||
|
_cameraFlash.transform = transform;
|
||
|
_cameraFlash.center = CGPointMake(_topBarView.center.x * 0.40, _topBarView.center.y);
|
||
|
|
||
|
_cameraToggle.transform = transform;
|
||
|
_cameraToggle.center = CGPointMake(_topBarView.center.x * 0.75, _topBarView.center.y);
|
||
|
|
||
|
_cameraDismiss.center = CGPointMake(_topBarView.center.x * 1.75, _topBarView.center.y);
|
||
|
}];
|
||
|
}
|
||
|
break;
|
||
|
default:;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#pragma mark - Camera Session Manager Delegate Methods
|
||
|
|
||
|
-(void)cameraSessionManagerDidCaptureImage
|
||
|
{
|
||
|
if (self.delegate)
|
||
|
{
|
||
|
if ([self.delegate respondsToSelector:@selector(didCaptureImage:)])
|
||
|
[self.delegate didCaptureImage:[[self captureManager] stillImage]];
|
||
|
|
||
|
if ([self.delegate respondsToSelector:@selector(didCaptureImageWithData:)])
|
||
|
[self.delegate didCaptureImageWithData:[[self captureManager] stillImageData]];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
-(void)cameraSessionManagerFailedToCaptureImage {
|
||
|
}
|
||
|
|
||
|
-(void)cameraSessionManagerDidReportAvailability:(BOOL)deviceAvailability forCameraType:(CameraType)cameraType {
|
||
|
}
|
||
|
|
||
|
-(void)cameraSessionManagerDidReportDeviceStatistics:(CameraStatistics)deviceStatistics {
|
||
|
}
|
||
|
|
||
|
#pragma mark - Helper Methods
|
||
|
|
||
|
- (void)focusAtPoint:(CGPoint)point completionHandler:(void(^)())completionHandler
|
||
|
{
|
||
|
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];;
|
||
|
CGPoint pointOfInterest = CGPointZero;
|
||
|
CGSize frameSize = self.bounds.size;
|
||
|
pointOfInterest = CGPointMake(point.y / frameSize.height, 1.f - (point.x / frameSize.width));
|
||
|
|
||
|
if ([device isFocusPointOfInterestSupported] && [device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
|
||
|
|
||
|
//Lock camera for configuration if possible
|
||
|
NSError *error;
|
||
|
if ([device lockForConfiguration:&error]) {
|
||
|
|
||
|
if ([device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeAutoWhiteBalance]) {
|
||
|
[device setWhiteBalanceMode:AVCaptureWhiteBalanceModeAutoWhiteBalance];
|
||
|
}
|
||
|
|
||
|
if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) {
|
||
|
[device setFocusMode:AVCaptureFocusModeAutoFocus];
|
||
|
[device setFocusPointOfInterest:pointOfInterest];
|
||
|
}
|
||
|
|
||
|
if([device isExposurePointOfInterestSupported] && [device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
|
||
|
[device setExposurePointOfInterest:pointOfInterest];
|
||
|
[device setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
|
||
|
}
|
||
|
|
||
|
[device unlockForConfiguration];
|
||
|
|
||
|
completionHandler();
|
||
|
}
|
||
|
}
|
||
|
else { completionHandler(); }
|
||
|
}
|
||
|
|
||
|
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
|
||
|
{
|
||
|
return (interfaceOrientation == UIInterfaceOrientationPortrait);
|
||
|
}
|
||
|
|
||
|
-(BOOL)shouldAutorotate
|
||
|
{
|
||
|
return YES;
|
||
|
}
|
||
|
|
||
|
-(NSUInteger)supportedInterfaceOrientations
|
||
|
{
|
||
|
return UIInterfaceOrientationMaskPortrait;
|
||
|
}
|
||
|
|
||
|
-(void)viewDidDisappear:(BOOL)animated{
|
||
|
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
|
||
|
}
|
||
|
|
||
|
#pragma mark - API Functions
|
||
|
|
||
|
- (void)setTopBarColor:(UIColor *)topBarColor
|
||
|
{
|
||
|
_topBarView.backgroundColor = topBarColor;
|
||
|
}
|
||
|
|
||
|
- (void)hideFlashButton
|
||
|
{
|
||
|
_cameraFlash.hidden = YES;
|
||
|
}
|
||
|
|
||
|
- (void)hideCameraToggleButton
|
||
|
{
|
||
|
_cameraToggle.hidden = YES;
|
||
|
}
|
||
|
|
||
|
- (void)hideDismissButton
|
||
|
{
|
||
|
_cameraDismiss.hidden = YES;
|
||
|
}
|
||
|
|
||
|
- (void)dealloc
|
||
|
{
|
||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||
|
}
|
||
|
|
||
|
@end
|