// // MTChatRecordInputView.m // Meet // // Created by ko1o on 2018/9/12. // Copyright © 2018年 ko1o. All rights reserved. // #import "MTChatRecordInputView.h" #import "MTAudioManager.h" #import "SVProgressHUD.h" #import #import "MTCameraTools.h" #import "ZZCircleProgress.h" #import "AudioPlayerView.h" #import "UIImage+Extender.h" static NSString *const kRecordNormalTips = @"点击录音"; static NSString *const kRecordRecordingTips = @"正在录制"; static NSString *const kRecordDoneTips = @"录制完成"; const NSTimeInterval MTChatAudioMaxDuration = 59.0; const NSTimeInterval MTChatAudioMinDuration = 5; @interface MTChatRecordInputView() @property(nonatomic,strong) UILongPressGestureRecognizer *longPressGesture; @property(nonatomic,assign) BOOL isNeedSend; @property (nonatomic, strong) UILabel *tipsLabel; @property (nonatomic, strong) UILabel *timeLabel; @property (nonatomic, strong) UIView *scaleBgView; @property (nonatomic, strong) UIButton *recordButton; @property (nonatomic, strong) ZZCircleProgress *progressView; @property (nonatomic, assign) BOOL recordComplete; @property (nonatomic, strong) UIButton *deleteButton; // 删除音频 @property (nonatomic, strong) UIButton *sendButton; // 发送音频 @property (nonatomic, strong) NSTimer *timer; @property (nonatomic, assign) int duration; @property (nonatomic, assign) NSTimeInterval recoredDuration; @property (nonatomic, strong) NSString *recordedFilePath; @property (nonatomic, assign) BOOL isFromLongPress; @end @implementation MTChatRecordInputView - (void)dealloc { [self stopRecord]; [self endTimmer]; } - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if(self) { self.tipsLabel = [[UILabel alloc] init]; self.tipsLabel.textColor = CONTENT_COLOR; self.tipsLabel.y = 15; self.tipsLabel.height = 30; self.tipsLabel.width = self.width; self.tipsLabel.font = MT_FONT_REGULAR_SIZE(16); self.tipsLabel.text = kRecordNormalTips; self.tipsLabel.textAlignment = NSTextAlignmentCenter; [self addSubview:self.tipsLabel]; self.timeLabel = [[UILabel alloc] init]; self.timeLabel.textColor = CONTENT_COLOR; self.tipsLabel.font = MT_FONT_REGULAR_SIZE(15); self.timeLabel.height = 20; self.timeLabel.y = self.tipsLabel.bottom + 5; self.timeLabel.width = self.width; self.timeLabel.textAlignment = NSTextAlignmentCenter; [self addSubview:self.timeLabel]; self.recordButton = [[UIButton alloc] init]; self.recordButton.adjustsImageWhenHighlighted = NO; self.recordButton.size = CGSizeMake(70, 70); [self.recordButton setImageEdgeInsets:UIEdgeInsetsMake(15, 15, 15, 15)]; [self.recordButton setImage:ImageNamed(@"TH_SaveMic") forState:UIControlStateNormal]; [self.recordButton setImage:ImageNamed(@"TH_orange_pause") forState:UIControlStateSelected]; [self.recordButton setBackgroundImage:ImageNamed(@"TH_square_btn_recording") forState:UIControlStateNormal]; [self.recordButton setBackgroundImage:[UIImage imageWithColor:UIColor.clearColor size:self.recordButton.size] forState:UIControlStateSelected]; WeakSelf(self); [self.recordButton addTapWithAction:^{ [MTCameraTools isMicrophoneAuthorized:^(BOOL authorized, BOOL thisTimeDetermined) { if (authorized && !thisTimeDetermined) { //本次允许授权,需再点击一次开始录音,否则手势不释放 weakself.recordButton.selected = !weakself.recordButton.selected; weakself.tipsLabel.text = weakself.recordButton.selected ? kRecordRecordingTips : kRecordNormalTips; if (weakself.recordComplete) { weakself.tipsLabel.text = kRecordDoneTips; } if (weakself.recordButton.selected) { if (!weakself.recordComplete) { // [weakself.recordButton removeGestureRecognizer:self.longPressGesture]; [weakself startRecord]; } [weakself startTimer]; } else { if (!weakself.recordComplete) { // 结束录制 weakself.isNeedSend = YES; [weakself stopRecord]; } else { // 停止播放 [weakself endTimmer]; } } } }]; }]; self.recordButton.layer.cornerRadius = self.recordButton.height * 0.5; self.recordButton.clipsToBounds = YES; self.recordButton.y = self.timeLabel.bottom + 30; self.recordButton.centerX = self.width * 0.5; [self addSubview:self.recordButton]; self.progressView = [[ZZCircleProgress alloc] initWithFrame:self.recordButton.frame pathBackColor:UIColor.clearColor pathFillColor:THEME_COLOR startAngle:-90 strokeWidth:10]; self.progressView.strokeWidth = 5.0; self.progressView.layer.cornerRadius = self.progressView.height * 0.5; self.progressView.clipsToBounds = YES; self.progressView.backgroundColor = UIColor.whiteColor; self.progressView.duration = 0.25; self.progressView.showPoint = NO; self.progressView.showProgressText = NO; self.progressView.increaseFromLast = YES; [self insertSubview:self.progressView belowSubview:self.recordButton]; self.progressView.prepareToShow = YES; self.progressView.hidden = YES; self.scaleBgView = [[UIView alloc] initWithFrame:CGRectZero]; self.scaleBgView.width = self.recordButton.width + 20; self.scaleBgView.height = self.recordButton.height + 20; self.scaleBgView.backgroundColor = COLOR_WITH_RGB(0xFFBE00); self.scaleBgView.layer.cornerRadius = self.scaleBgView.height * 0.5; self.scaleBgView.center = self.recordButton.center; self.scaleBgView.alpha = 0.1; [self insertSubview:self.scaleBgView aboveSubview:self.progressView]; self.deleteButton = [UIButton buttonWithType:UIButtonTypeSystem]; self.deleteButton.size = CGSizeMake(60, 60); [self.deleteButton setImageEdgeInsets:UIEdgeInsetsMake(8, 8, 8, 8)]; [self.deleteButton setImage:[ImageNamed(@"TH_Delete_gray") imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:UIControlStateNormal]; self.deleteButton.centerY = self.recordButton.centerY; self.deleteButton.right = self.recordButton.x - 50; [self.deleteButton addTouchUpInsideWithAction:^(UIButton * _Nullable button) { [weakself resetRecord]; if ([[NSFileManager defaultManager] fileExistsAtPath:weakself.recordedFilePath]) { [[NSFileManager defaultManager] removeItemAtPath:weakself.recordedFilePath error:nil]; } }]; [self addSubview:self.deleteButton]; self.sendButton = [UIButton buttonWithType:UIButtonTypeSystem]; self.sendButton.size = CGSizeMake(60, 60); [self.sendButton setImageEdgeInsets:UIEdgeInsetsMake(8, 8, 8, 8)]; [self.sendButton setImage:[ImageNamed(@"select_orange_btn") imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal] forState:UIControlStateNormal]; self.sendButton.centerY = self.recordButton.centerY; self.sendButton.x = self.recordButton.right + 50; [self addSubview:self.sendButton]; [self.sendButton addTouchUpInsideWithAction:^(UIButton * _Nullable button) { if([weakself.delegate respondsToSelector:@selector(outputRecordPath:recordLength:)]) { [weakself.delegate outputRecordPath:weakself.recordedFilePath recordLength:weakself.recoredDuration]; } if (weakself.completion) { weakself.completion(weakself.recordedFilePath, weakself.recoredDuration); } [weakself endTimmer]; [weakself resetRecord]; }]; self.deleteButton.hidden = self.sendButton.hidden = YES; // _longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressToRecord:)]; // _longPressGesture.minimumPressDuration = 0.2; // [self.recordButton addGestureRecognizer:self.longPressGesture]; } return self; } - (void)startTimer { self.duration = 0; self.timeLabel.hidden = NO; self.timeLabel.text = @"00 : 00"; self.progressView.hidden = NO; self.recordButton.selected = YES; self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(recording) userInfo:nil repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes]; [self.timer fire]; [self beginScaleAnimation]; if (self.recordComplete) { self.deleteButton.hidden = self.sendButton.hidden = NO; [self play]; } else { self.deleteButton.hidden = self.sendButton.hidden = YES; } } - (void)recording { int min = self.duration / 60; int second = self.duration % 60; self.timeLabel.text = [NSString stringWithFormat:@"%02d : %02d", min, second];; CGFloat progress = self.duration / (self.recordComplete ? self.recoredDuration : MTChatAudioMaxDuration); self.progressView.progress = progress; self.duration ++; } - (void)endTimmer { self.timeLabel.hidden = YES; self.duration = 0; [self.timer invalidate]; self.timer = nil; self.recordButton.selected = NO; self.progressView.progress = 0.0; self.progressView.hidden = YES; self.tipsLabel.textColor = CONTENT_COLOR; [self.scaleBgView.layer removeAllAnimations]; if (self.recordComplete) { self.tipsLabel.text = kRecordDoneTips; [self stopPlay]; } else { self.tipsLabel.text = kRecordNormalTips; } } - (void)beginScaleAnimation { CABasicAnimation *scaleAnimiation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; scaleAnimiation.removedOnCompletion = NO; scaleAnimiation.duration = 0.75; scaleAnimiation.repeatCount = INTMAX_MAX; // 重复次数 scaleAnimiation.autoreverses = YES; // 动画结束时执行逆动画 scaleAnimiation.fillMode = kCAFillModeForwards; scaleAnimiation.toValue = [NSNumber numberWithFloat:1.1]; [self.scaleBgView.layer addAnimation:scaleAnimiation forKey:nil]; } #pragma mark - press gesture - (void)longPressToRecord:(UILongPressGestureRecognizer *)longPressGesture { if (self.recordComplete) { return; } CGPoint point = [longPressGesture locationInView:self.recordButton]; if(longPressGesture.state == UIGestureRecognizerStateBegan) { self.isFromLongPress = YES; [MTCameraTools isMicrophoneAuthorized:^(BOOL authorized, BOOL thisTimeDetermined) { if (authorized && !thisTimeDetermined) { //本次允许授权,需再点击一次开始录音,否则手势不释放 self.isNeedSend = YES; [self startTimer]; self.tipsLabel.text = @"上滑取消"; self.tipsLabel.textColor = CONTENT_COLOR; [self startRecord]; [self.superview.window addSubview:self.maskView]; } }]; } else if(longPressGesture.state == UIGestureRecognizerStateChanged) { BOOL contain = CGRectContainsPoint(self.recordButton.bounds, point); if (!contain) { // 超出区域 self.tipsLabel.text = @"松开取消录制"; self.isNeedSend = NO; self.tipsLabel.textColor = UIColor.redColor; } else { self.tipsLabel.text = @"上滑取消"; self.tipsLabel.textColor = CONTENT_COLOR; self.isNeedSend = YES; } } else if(longPressGesture.state == UIGestureRecognizerStateEnded) { self.isFromLongPress = YES; [self stopRecord]; self.longPressGesture.enabled = YES;//超时长录音停止,手势被打断后恢复 } else { self.tipsLabel.text = kRecordNormalTips; self.tipsLabel.textColor = CONTENT_COLOR; [self endTimmer]; self.longPressGesture.enabled = YES; } } - (void)resetRecord { self.recoredDuration = 0; self.recordComplete = NO; self.isNeedSend = NO; self.isFromLongPress = NO; [self endTimmer]; self.tipsLabel.text = kRecordNormalTips; [self.recordButton setImage:ImageNamed(@"TH_SaveMic") forState:UIControlStateNormal]; self.deleteButton.hidden = self.sendButton.hidden = YES; // [self.recordButton addGestureRecognizer:self.longPressGesture]; } - (void)layoutRecordEnded { self.recordComplete = YES; [self.recordButton setImage:ImageNamed(@"TH_videoPlayBtn") forState:UIControlStateNormal]; self.deleteButton.hidden = self.sendButton.hidden = NO; [self endTimmer]; self.tipsLabel.text = kRecordDoneTips; } - (BOOL)startRecord { [AudioPlayerView stopPlay]; [MTAudioManager sharedInstance].recordDelegate = self; if (![[MTAudioManager sharedInstance] initRecord]) { [SVProgressHUD showErrorWithStatus:@"录制失败"]; [self resetRecord]; } if (![[MTAudioManager sharedInstance] startRecord]) { [SVProgressHUD showErrorWithStatus:@"录制失败"]; [self resetRecord]; } return YES; } - (void)stopRecord { [[MTAudioManager sharedInstance] stopRecord]; } - (void)willMoveToWindow:(UIWindow *)newWindow { if (newWindow == nil) { [self stopRecord]; } } // 播放音频 - (void)play { NSData *data = [NSData dataWithContentsOfFile:self.recordedFilePath]; [[MTAudioManager sharedInstance] playWithData:data finish:^{ [self endTimmer]; }]; } // 停止播放 - (void)stopPlay { [[MTAudioManager sharedInstance] stopPlay]; } #pragma mark - MTAudioManagerRecordDelegate - (void)audioManagerDidStopedRecord:(MTAudioManager *)audioManager audioPath:(NSString *) recordedFilePath duration:(NSInteger)duration { if (!self.isNeedSend || duration <= 0.0) { [self resetRecord]; return; } if(self.window.hidden == NO && duration < MTChatAudioMinDuration) { [SVProgressHUD showErrorWithStatus:[NSString stringWithFormat:@"录音时间不能少于%.0f秒", MTChatAudioMinDuration]]; [self resetRecord]; return; } self.recoredDuration = duration; self.recordedFilePath = recordedFilePath; if (self.isNeedSend && self.isFromLongPress) { // 直接发送 if([self.delegate respondsToSelector:@selector(outputRecordPath:recordLength:)]) { [self.delegate outputRecordPath:self.recordedFilePath recordLength:self.recoredDuration]; } if (self.completion) { self.completion(self.recordedFilePath, self.recoredDuration); } [self resetRecord]; } else { [self layoutRecordEnded]; } } - (void)audioManager:(MTAudioManager *)audioManager audioPath:(NSString *)audioPath recordingProgress:(NSTimeInterval)duration { if (duration >= MTChatAudioMaxDuration) { //超过录音时长 self.isNeedSend = YES; [SVProgressHUD showInfoWithStatus:[NSString stringWithFormat:@"录音最长%.0f秒", MTChatAudioMaxDuration]]; // 结束录音 [self stopRecord]; } } @end