| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- // Created by Tencent on 2023/06/09.
- // Copyright © 2023 Tencent. All rights reserved.
- #import "TUICameraView.h"
- #import <TIMCommon/TIMCommonModel.h>
- #import <TIMCommon/TIMDefine.h>
- #import "TUICaptureTimer.h"
- static CGFloat gPhotoBtnZoomInRatio = 1.125;
- static CGFloat gProgressLayerLineWidth = 5.0;
- @interface TUICameraView ()
- @property(nonatomic) UIView *contentView;
- @property(nonatomic) UIButton *switchCameraButton;
- @property(nonatomic) UIButton *closeButton;
- @property(nonatomic, strong) UIButton *pictureLibButton;
- @property(nonatomic) UIView *focusView;
- @property(nonatomic) UISlider *slider;
- @property(nonatomic) UIView *photoBtn;
- @property(nonatomic) UIView *photoStateView;
- @property(nonatomic) UILongPressGestureRecognizer *longPress;
- @property(nonatomic) CGRect lastRect;
- @property(nonatomic) CAShapeLayer *progressLayer;
- @property(nonatomic) TUICaptureTimer *timer;
- @property(nonatomic) BOOL isVideoRecording;
- @end
- @implementation TUICameraView
- @synthesize previewView = _previewView;
- - (instancetype)initWithFrame:(CGRect)frame {
- self = [super initWithFrame:frame];
- if (self) {
- self.type = TUICameraMediaTypePhoto;
- self.aspectRatio = TUICameraViewAspectRatio16x9;
- self.backgroundColor = [UIColor blackColor];
- self.maxVideoCaptureTimeLimit = 15.0;
- }
- return self;
- }
- - (void)setupUI {
- [self addSubview:self.contentView];
- [self.contentView addSubview:self.previewView];
- [self.contentView addSubview:self.switchCameraButton];
- [self.contentView addSubview:self.photoBtn];
- [self.contentView addSubview:self.closeButton];
- [self.contentView addSubview:self.pictureLibButton];
- [self.previewView addSubview:self.focusView];
- [self.previewView addSubview:self.slider];
- self.timer = ({
- TUICaptureTimer *timer = [TUICaptureTimer new];
- timer.maxCaptureTime = self.maxVideoCaptureTimeLimit;
- __weak __typeof(self) weakSelf = self;
- timer.progressBlock = ^(CGFloat ratio, CGFloat recordTime) {
- weakSelf.progress = ratio;
- };
- timer.progressFinishBlock = ^(CGFloat ratio, CGFloat recordTime) {
- weakSelf.progress = 1;
- self.longPress.enabled = NO;
- [weakSelf endVideoRecordWithCaptureDuration:recordTime];
- self.longPress.enabled = YES;
- };
- timer.progressCancelBlock = ^{
- weakSelf.progress = 0;
- };
- timer;
- });
- UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapAction:)];
- [self.previewView addGestureRecognizer:tap];
- UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchAction:)];
- [self.previewView addGestureRecognizer:pinch];
- }
- - (void)layoutSubviews {
- [super layoutSubviews];
- if (!CGRectEqualToRect(self.lastRect, self.bounds)) {
- [self setupUI];
- self.lastRect = self.bounds;
- self.contentView.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
- CGFloat previewViewWidth = self.contentView.bounds.size.width;
- CGFloat previewViewHeight;
- switch (self.aspectRatio) {
- case TUICameraViewAspectRatio1x1:
- previewViewHeight = previewViewWidth;
- break;
- case TUICameraViewAspectRatio16x9:
- previewViewHeight = previewViewWidth * (16.0 / 9.0);
- break;
- case TUICameraViewAspectRatio5x4:
- previewViewHeight = previewViewWidth * (5.0 / 4.0);
- break;
- default:
- break;
- }
- CGFloat previewViewY = (self.contentView.bounds.size.height - previewViewHeight) / 2.0;
- self.previewView.frame = CGRectMake(0, previewViewY, self.contentView.bounds.size.width, previewViewHeight);
- CGFloat switchCameraButtonWidth = 44.0;
- self.switchCameraButton.frame =
- CGRectMake(self.contentView.bounds.size.width - switchCameraButtonWidth - 16.0, 30.0, switchCameraButtonWidth, switchCameraButtonWidth);
- if (isRTL()) {
- [self.switchCameraButton resetFrameToFitRTL];
- }
-
- CGFloat photoBtnWidth = 100.0;
- self.photoBtn.frame = CGRectMake((self.contentView.bounds.size.width - photoBtnWidth) / 2.0, self.contentView.bounds.size.height - photoBtnWidth - 30,
- photoBtnWidth, photoBtnWidth);
- self.photoBtn.layer.cornerRadius = photoBtnWidth / 2.0;
- CGFloat distanceToPhotoBtn = 10.0;
- CGFloat photoStateViewWidth = photoBtnWidth - 2 * distanceToPhotoBtn;
- self.photoStateView.frame = CGRectMake(distanceToPhotoBtn, distanceToPhotoBtn, photoStateViewWidth, photoStateViewWidth);
- self.photoStateView.layer.cornerRadius = photoStateViewWidth / 2.0;
- if (self.type == TUICameraMediaTypeVideo) {
- self.progressLayer.frame = CGRectInset(self.photoBtn.bounds, gProgressLayerLineWidth / 2.0, gProgressLayerLineWidth / 2.0);
- CGFloat radius = self.progressLayer.bounds.size.width / 2;
- UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(radius, radius)
- radius:radius
- startAngle:-M_PI_2
- endAngle:-M_PI_2 + M_PI * 2
- clockwise:YES];
- self.progressLayer.path = path.CGPath;
- [self.photoBtn.layer addSublayer:self.progressLayer];
- }
- CGFloat closeButtonWidth = 30.0;
- CGFloat closeButtonX = (self.photoBtn.frame.origin.x - closeButtonWidth) / 2.0;
- CGFloat closeButtonY = self.photoBtn.center.y - closeButtonWidth / 2.0;
- self.closeButton.frame = CGRectMake(closeButtonX, closeButtonY, closeButtonWidth, closeButtonWidth);
- if (isRTL()) {
- [self.closeButton resetFrameToFitRTL];
- }
- CGFloat pictureButtonWidth = 30.0;
- self.pictureLibButton.frame = CGRectMake(self.contentView.frame.size.width - closeButtonX, closeButtonY, pictureButtonWidth, pictureButtonWidth);
- if (isRTL()) {
- [self.pictureLibButton resetFrameToFitRTL];
- }
- self.slider.transform = CGAffineTransformMakeRotation(M_PI_2);
- self.slider.frame = CGRectMake(self.bounds.size.width - 50, 50, 15, 200);
- }
- }
- #pragma mark -
- - (void)setProgress:(CGFloat)progress {
- if (progress < 0) {
- return;
- } else if (progress < 1.0) {
- self.progressLayer.strokeEnd = progress;
- }
- if (progress >= 1.0) {
- self.progressLayer.strokeEnd = 1.0;
- }
- }
- #pragma mark - Event Response
- - (void)tapAction:(UIGestureRecognizer *)tap {
- if ([_delegate respondsToSelector:@selector(focusAction:point:handle:)]) {
- CGPoint point = [tap locationInView:self.previewView];
- [self runFocusAnimation:self.focusView point:point];
- [_delegate focusAction:self
- point:[self.previewView captureDevicePointForPoint:point]
- handle:^(NSError *error) {
- if (error) NSAssert1(NO, @"%@", error); //[self showError:error];
- }];
- }
- }
- - (void)runFocusAnimation:(UIView *)view point:(CGPoint)point {
- view.center = point;
- view.hidden = NO;
- [UIView animateWithDuration:0.15f
- delay:0.0f
- options:UIViewAnimationOptionCurveEaseInOut
- animations:^{
- view.layer.transform = CATransform3DMakeScale(0.5, 0.5, 1.0);
- }
- completion:^(BOOL complete) {
- double delayInSeconds = 0.5f;
- dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
- dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
- view.hidden = YES;
- view.transform = CGAffineTransformIdentity;
- });
- }];
- }
- - (void)pinchAction:(UIPinchGestureRecognizer *)pinch {
- if ([_delegate respondsToSelector:@selector(zoomAction:factor:)]) {
- if (pinch.state == UIGestureRecognizerStateBegan) {
- [UIView animateWithDuration:0.1
- animations:^{
- self->_slider.alpha = 1;
- }];
- } else if (pinch.state == UIGestureRecognizerStateChanged) {
- if (pinch.velocity > 0) {
- _slider.value += pinch.velocity / 100;
- } else {
- _slider.value += pinch.velocity / 20;
- }
- [_delegate zoomAction:self factor:powf(5, _slider.value)];
- } else {
- [UIView animateWithDuration:0.1
- animations:^{
- self->_slider.alpha = 0.0;
- }];
- }
- }
- }
- - (void)switchCameraButtonClick:(UIButton *)btn {
- if ([self.delegate respondsToSelector:@selector(swicthCameraAction:handle:)]) {
- [self.delegate swicthCameraAction:self
- handle:^(NSError *_Nonnull error){
- //
- }];
- }
- }
- - (void)closeButtonClick:(UIButton *)btn {
- if ([self.delegate respondsToSelector:@selector(cancelAction:)]) {
- [self.delegate cancelAction:self];
- }
- }
- - (void)pictureLibClick:(UIButton *)btn {
- if ([self.delegate respondsToSelector:@selector(pictureLibAction:)]) {
- [self.delegate pictureLibAction:self];
- }
- }
- - (void)longPressGesture:(UILongPressGestureRecognizer *)gesture {
- switch (gesture.state) {
- case UIGestureRecognizerStateBegan: {
- [self beginVideoRecord];
- break;
- }
- case UIGestureRecognizerStateChanged: {
- break;
- }
- case UIGestureRecognizerStateEnded: {
- [self endVideoRecordWithCaptureDuration:self.timer.captureDuration];
- break;
- }
- default:
- break;
- }
- }
- - (void)beginVideoRecord {
- if (self.isVideoRecording) {
- return;
- }
- self.closeButton.hidden = YES;
- self.isVideoRecording = YES;
- self.pictureLibButton.hidden = YES;
- [self.timer startTimer];
- dispatch_async(dispatch_get_main_queue(), ^{
- self.progressLayer.strokeEnd = 0.0;
- [UIView animateWithDuration:0.2
- animations:^{
- self.photoStateView.transform = CGAffineTransformMakeScale(.5, .5);
- self.photoBtn.transform = CGAffineTransformMakeScale(gPhotoBtnZoomInRatio, gPhotoBtnZoomInRatio);
- }];
- if ([self.delegate respondsToSelector:@selector(startRecordVideoAction:)]) {
- [self.delegate startRecordVideoAction:self];
- }
- });
- }
- - (void)endVideoRecordWithCaptureDuration:(CGFloat)duration {
- if (self.isVideoRecording == NO) {
- return;
- }
- self.closeButton.hidden = NO;
- self.isVideoRecording = NO;
- self.pictureLibButton.hidden = NO;
- [self.timer stopTimer];
- dispatch_async(dispatch_get_main_queue(), ^{
- [UIView animateWithDuration:0.2
- animations:^{
- self.photoStateView.transform = CGAffineTransformIdentity;
- self.photoBtn.transform = CGAffineTransformIdentity;
- }];
- if ([self.delegate respondsToSelector:@selector(stopRecordVideoAction:RecordDuration:)]) {
- [self.delegate stopRecordVideoAction:self RecordDuration:duration];
- }
- self.progressLayer.strokeEnd = 0.0;
- });
- }
- - (void)tapGesture:(UITapGestureRecognizer *)tapGesture {
- if ([_delegate respondsToSelector:@selector(takePhotoAction:)]) {
- [_delegate takePhotoAction:self];
- }
- }
- #pragma mark - Getters & Setters
- - (UIView *)contentView {
- if (!_contentView) {
- _contentView = [UIView new];
- }
- return _contentView;
- }
- - (TUICaptureVideoPreviewView *)previewView {
- if (!_previewView) {
- _previewView = [[TUICaptureVideoPreviewView alloc] init];
- _previewView.userInteractionEnabled = YES;
- }
- return _previewView;
- }
- - (UIButton *)switchCameraButton {
- if (!_switchCameraButton) {
- _switchCameraButton = [UIButton buttonWithType:UIButtonTypeCustom];
- UIImage *switchCameraButtonImage = [[TUIImageCache sharedInstance] getResourceFromCache:TUIChatImagePath(@"camera_switchCamera")];
- [_switchCameraButton setImage:switchCameraButtonImage forState:UIControlStateNormal];
- [_switchCameraButton addTarget:self action:@selector(switchCameraButtonClick:) forControlEvents:UIControlEventTouchUpInside];
- }
- return _switchCameraButton;
- }
- - (UIButton *)closeButton {
- if (!_closeButton) {
- _closeButton = [UIButton buttonWithType:UIButtonTypeCustom];
- UIImage *closeButtonImage = [[TUIImageCache sharedInstance] getResourceFromCache:TUIChatImagePath(@"camera_back")];
- [_closeButton setBackgroundImage:[closeButtonImage rtl_imageFlippedForRightToLeftLayoutDirection] forState:UIControlStateNormal];
- [_closeButton addTarget:self action:@selector(closeButtonClick:) forControlEvents:UIControlEventTouchUpInside];
- }
- return _closeButton;
- }
- - (UIButton *)pictureLibButton {
- if (!_pictureLibButton) {
- _pictureLibButton = [UIButton buttonWithType:UIButtonTypeCustom];
- UIImage *pictureImage = [[TUIImageCache sharedInstance] getResourceFromCache:TUIChatImagePath(@"more_picture")];
- [_pictureLibButton setBackgroundImage:pictureImage forState:UIControlStateNormal];
- [_pictureLibButton addTarget:self action:@selector(pictureLibClick:) forControlEvents:UIControlEventTouchUpInside];
- }
- return _pictureLibButton;
- }
- - (UIView *)photoBtn {
- if (!_photoBtn) {
- _photoBtn = [UIView new];
- [_photoBtn setBackgroundColor:[[UIColor whiteColor] colorWithAlphaComponent:0.5]];
- if (self.type == TUICameraMediaTypeVideo) {
- UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressGesture:)];
- [_photoBtn addGestureRecognizer:longPress];
- _longPress = longPress;
- }
- if (self.type == TUICameraMediaTypePhoto) {
- UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGesture:)];
- [_photoBtn addGestureRecognizer:tapGesture];
- }
- _photoBtn.userInteractionEnabled = YES;
- _photoStateView = [UIView new];
- _photoStateView.backgroundColor = [UIColor whiteColor];
- [_photoBtn addSubview:_photoStateView];
- }
- return _photoBtn;
- }
- - (CAShapeLayer *)progressLayer {
- if (!_progressLayer) {
- _progressLayer = [CAShapeLayer layer];
- _progressLayer.fillColor = [UIColor clearColor].CGColor;
- _progressLayer.lineWidth = gProgressLayerLineWidth;
- _progressLayer.strokeColor = [UIColor colorWithRed:0 green:204.0 / 255 blue:0 alpha:1].CGColor;
- _progressLayer.strokeStart = 0;
- _progressLayer.strokeEnd = 0;
- _progressLayer.lineCap = kCALineCapButt;
- }
- return _progressLayer;
- }
- - (UIView *)focusView {
- if (_focusView == nil) {
- _focusView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 150, 150.0f)];
- _focusView.backgroundColor = [UIColor clearColor];
- _focusView.layer.borderColor = [UIColor colorWithRed:0 green:204.0 / 255 blue:0 alpha:1].CGColor;
- _focusView.layer.borderWidth = 3.0f;
- _focusView.hidden = YES;
- }
- return _focusView;
- }
- - (UISlider *)slider {
- if (_slider == nil) {
- _slider = [[UISlider alloc] init];
- _slider.minimumValue = 0;
- _slider.maximumValue = 1;
- _slider.maximumTrackTintColor = [UIColor whiteColor];
- _slider.minimumTrackTintColor = [UIColor whiteColor];
- _slider.alpha = 0.0;
- _slider.hidden = YES;
- }
- return _slider;
- }
- @end
|