419 lines
15 KiB
Objective-C
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
|