class
TGCameraPreviewView
@property (nonatomic, readonly) TGPGCamera *camera;
@property (nonatomic, readonly) AVCaptureConnection *captureConnection;
- (void)setupWithCamera:(TGPGCamera *)camera;
- (void)invalidate;
- (void)beginTransitionWithSnapshotImage:(UIImage *)image animated:(bool)animated;
- (void)endTransitionAnimated:(bool)animated;
- (void)beginResetTransitionAnimated:(bool)animated;
- (void)endResetTransitionAnimated:(bool)animated;
- (void)fadeInAnimated:(bool)animated;
- (void)fadeOutAnimated:(bool)animated;
- (void)blink;
- (CGPoint)devicePointOfInterestForPoint:(CGPoint)point;
// private extension
UIView<TGCameraPreviewLayerView> *_wrapperView;
UIView *_fadeView;
UIView *_snapshotView;
TGPGCamera *_camera;
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self != nil)
{
self.backgroundColor = [UIColor blackColor];
self.clipsToBounds = true;
if (false && iosMajorVersion() >= 8)
_wrapperView = [[TGCameraPreviewLayerWrapperView alloc] init];
else
_wrapperView = [[TGCameraLegacyPreviewLayerWrapperView alloc] init];
[self addSubview:_wrapperView];
_wrapperView.videoGravity = AVLayerVideoGravityResizeAspectFill;
_fadeView = [[UIView alloc] initWithFrame:self.bounds];
_fadeView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_fadeView.backgroundColor = [UIColor blackColor];
_fadeView.userInteractionEnabled = false;
[self addSubview:_fadeView];
if (iosMajorVersion() >= 11)
_fadeView.accessibilityIgnoresInvertColors = true;
#if TARGET_IPHONE_SIMULATOR
_fadeView.backgroundColor = [UIColor redColor];
#endif
}
return self;
}
- (AVCaptureConnection *)captureConnection
{
return _wrapperView.connection;
}
- (AVSampleBufferDisplayLayer *)displayLayer
{
return _wrapperView.displayLayer;
}
- (AVCaptureVideoPreviewLayer *)legacyPreviewLayer
{
return _wrapperView.previewLayer;
}
// setupCamera
- (void)setupWithCamera:(TGPGCamera *)camera
{
_camera = camera;
__weak TGCameraPreviewView *weakSelf = self;
if ([_wrapperView isKindOfClass:[TGCameraPreviewLayerWrapperView class]])
{
[self.displayLayer flushAndRemoveImage];
camera.captureSession.outputSampleBuffer = ^(CMSampleBufferRef buffer, AVCaptureConnection *connection)
{
__strong TGCameraPreviewView *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[(TGCameraPreviewLayerWrapperView *)strongSelf->_wrapperView enqueueSampleBuffer:buffer connection:connection];
};
}
else
{
#if !TARGET_IPHONE_SIMULATOR
[self.legacyPreviewLayer setSession:camera.captureSession];
#endif
}
camera.captureStarted = ^(bool resume)
{
__strong TGCameraPreviewView *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (resume)
[strongSelf endResetTransitionAnimated:true];
else
[strongSelf fadeInAnimated:true];
};
camera.captureStopped = ^(bool pause)
{
__strong TGCameraPreviewView *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (pause)
[strongSelf beginResetTransitionAnimated:true];
else
[strongSelf fadeOutAnimated:true];
};
}
// transition animation
- (void)beginResetTransitionAnimated:(bool)animated
{
if (iosMajorVersion() < 7)
return;
[_snapshotView removeFromSuperview];
_snapshotView = [_wrapperView snapshotViewAfterScreenUpdates:false];
_snapshotView.frame = _wrapperView.frame;
[self insertSubview:_snapshotView aboveSubview:_wrapperView];
if (animated)
{
_snapshotView.alpha = 0.0f;
[UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut animations:^
{
_snapshotView.alpha = 1.0f;
} completion:nil];
}
}
- (void)endResetTransitionAnimated:(bool)animated
{
if (iosMajorVersion() < 7)
return;
if (animated)
{
UIView *snapshotView = _snapshotView;
_snapshotView = nil;
[UIView animateWithDuration:0.4f delay:0.05f options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionBeginFromCurrentState animations:^
{
snapshotView.alpha = 0.0f;
} completion:^(__unused BOOL finished)
{
[snapshotView removeFromSuperview];
}];
}
else
{
[_snapshotView removeFromSuperview];
_snapshotView = nil;
}
}
// blink
- (void)blink
{
[UIView animateWithDuration:0.07f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^
{
_fadeView.alpha = 1.0f;
} completion:^(BOOL finished)
{
[UIView animateWithDuration:0.07f delay:0.0f options:UIViewAnimationOptionCurveLinear animations:^
{
_fadeView.alpha = 0.0f;
} completion:^(BOOL finished)
{
}];
}];
}
// fade in
- (void)fadeInAnimated:(bool)animated
{
if (animated)
{
[UIView animateWithDuration:0.3f delay:0.05f options:UIViewAnimationOptionCurveLinear animations:^
{
_fadeView.alpha = 0.0f;
} completion:nil];
}
else
{
_fadeView.alpha = 0.0f;
}
}
// fade out
- (void)fadeOutAnimated:(bool)animated
{
if (animated)
{
[UIView animateWithDuration:0.3f animations:^
{
_fadeView.alpha = 1.0f;
}];
}
else
{
_fadeView.alpha = 1.0f;
}
}
// invalidate
- (void)invalidate
{
if ([_wrapperView isKindOfClass:[TGCameraPreviewLayerWrapperView class]])
{
[self.displayLayer flushAndRemoveImage];
_camera.captureSession.outputSampleBuffer = nil;
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
[self.legacyPreviewLayer setSession:nil];
});
}
_wrapperView = nil;
}
protocol
TGCameraPreviewLayerView
@property (nonatomic, strong) NSString *videoGravity;
@property (nonatomic, readonly) AVCaptureConnection *connection;
- (CGPoint)captureDevicePointOfInterestForPoint:(CGPoint)point;
@optional
- (AVSampleBufferDisplayLayer *)displayLayer;
- (AVCaptureVideoPreviewLayer *)previewLayer;
class
TGCameraLegacyPreviewLayerWrapperView
@property (nonatomic, readonly) AVCaptureVideoPreviewLayer *previewLayer;
class
TGCameraPreviewLayerWrapperView
- (NSString *)videoGravity
{
return [self displayLayer].videoGravity;
}
- (void)setVideoGravity:(NSString *)videoGravity
{
self.displayLayer.videoGravity = videoGravity;
}
- (AVCaptureConnection *)connection
{
return _connection;
}
- (CGPoint)captureDevicePointOfInterestForPoint:(CGPoint)point
{
return CGPointZero;
}
- (void)enqueueSampleBuffer:(CMSampleBufferRef)buffer connection:(AVCaptureConnection *)connection
{
_connection = connection;
//self.orientation = connection.videoOrientation;
//self.mirrored = connection.videoMirrored;
[self.displayLayer enqueueSampleBuffer:buffer];
}
- (AVSampleBufferDisplayLayer *)displayLayer
{
return (AVSampleBufferDisplayLayer *)self.layer;
}
+ (Class)layerClass
{
return [AVSampleBufferDisplayLayer class];
}