cdts/xdts-ios 3/TreeHole/Code/Utility/VoiceRecoder/MTChatRecordInputView.m
2023-07-27 09:20:00 +08:00

419 lines
15 KiB
Objective-C

//
// 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 <AVFoundation/AVCaptureDevice.h>
#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()<MTAudioManagerRecordDelegate>
@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