diff --git a/ChatKit/ChatKit.xcodeproj/project.pbxproj b/ChatKit/ChatKit.xcodeproj/project.pbxproj index 6ef7ff83..29f2d795 100644 --- a/ChatKit/ChatKit.xcodeproj/project.pbxproj +++ b/ChatKit/ChatKit.xcodeproj/project.pbxproj @@ -502,6 +502,9 @@ 9AC44E9D1E15154300619112 /* UITableView+FDTemplateLayoutCellDebug.m in Sources */ = {isa = PBXBuildFile; fileRef = 9AC44D7D1E15154300619112 /* UITableView+FDTemplateLayoutCellDebug.m */; }; 9AC44E9E1E15154300619112 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 9AC44D7E1E15154300619112 /* LICENSE */; }; 9AC44E9F1E15154300619112 /* README.md in Sources */ = {isa = PBXBuildFile; fileRef = 9AC44D7F1E15154300619112 /* README.md */; }; + FAE19C971EEA8EE4003B95FE /* LCCKRecordAudioHUD.h in Headers */ = {isa = PBXBuildFile; fileRef = FAE19C951EEA8EE4003B95FE /* LCCKRecordAudioHUD.h */; }; + FAE19C981EEA8EE4003B95FE /* LCCKRecordAudioHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = FAE19C961EEA8EE4003B95FE /* LCCKRecordAudioHUD.m */; }; + FAE19C9E1EEA95CC003B95FE /* LCCKRecordAudioHUD.bundle in Resources */ = {isa = PBXBuildFile; fileRef = FAE19C9D1EEA95CC003B95FE /* LCCKRecordAudioHUD.bundle */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -1005,6 +1008,9 @@ 9AC44D7E1E15154300619112 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 9AC44D7F1E15154300619112 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; A9601FCE9729A0FE4669D33E /* libPods-ChatKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ChatKit.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + FAE19C951EEA8EE4003B95FE /* LCCKRecordAudioHUD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LCCKRecordAudioHUD.h; sourceTree = ""; }; + FAE19C961EEA8EE4003B95FE /* LCCKRecordAudioHUD.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LCCKRecordAudioHUD.m; sourceTree = ""; }; + FAE19C9D1EEA95CC003B95FE /* LCCKRecordAudioHUD.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = LCCKRecordAudioHUD.bundle; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1276,6 +1282,8 @@ 9AAFA1871E14FBA3006618E0 /* LCCKInputViewPluginTakePhoto.m */, 9AAFA1881E14FBA3006618E0 /* LCCKProgressHUD.h */, 9AAFA1891E14FBA3006618E0 /* LCCKProgressHUD.m */, + FAE19C951EEA8EE4003B95FE /* LCCKRecordAudioHUD.h */, + FAE19C961EEA8EE4003B95FE /* LCCKRecordAudioHUD.m */, ); path = ChatBar; sourceTree = ""; @@ -1354,6 +1362,7 @@ 9AAFA1B21E14FBA4006618E0 /* Other.bundle */, 9AAFA1B31E14FBA4006618E0 /* Placeholder.bundle */, 9AAFA1B41E14FBA4006618E0 /* VoiceMessageSource.bundle */, + FAE19C9D1EEA95CC003B95FE /* LCCKRecordAudioHUD.bundle */, ); path = Resources; sourceTree = ""; @@ -2137,6 +2146,7 @@ 9AAFA21A1E14FBA4006618E0 /* NSMutableArray+LCCKMessageExtention.h in Headers */, 9AAFA2631E14FBA5006618E0 /* LCCKInputViewPluginTakePhoto.h in Headers */, 9AC44E6F1E15154300619112 /* POPSpringSolver.h in Headers */, + FAE19C971EEA8EE4003B95FE /* LCCKRecordAudioHUD.h in Headers */, 9AAFA24B1E14FBA4006618E0 /* LCCKFaceManager.h in Headers */, 9AAFA2D01E14FBA6006618E0 /* Mp3Recorder.h in Headers */, 9AAFA2B71E14FBA5006618E0 /* LCCKAlertController.h in Headers */, @@ -2422,6 +2432,7 @@ 9AAFA2871E14FBA5006618E0 /* MBProgressHUD.bundle in Resources */, 9AC44E181E15154300619112 /* ImageSelectedSmallOn@2x.png in Resources */, 9AAFA2821E14FBA5006618E0 /* ChatKeyboard.bundle in Resources */, + FAE19C9E1EEA95CC003B95FE /* LCCKRecordAudioHUD.bundle in Resources */, 9AC44E9E1E15154300619112 /* LICENSE in Resources */, 9AC44E0D1E15154300619112 /* ImageError@3x.png in Resources */, 9AC44E231E15154300619112 /* UIBarButtonItemArrowRight.png in Resources */, @@ -2556,6 +2567,7 @@ 9AC44DC51E15154300619112 /* ViewController+MASAdditions.m in Sources */, 9AAFA2D91E14FBA6006618E0 /* LCCKSwipeView.m in Sources */, 9AC44DCF1E15154300619112 /* MJRefreshBackFooter.m in Sources */, + FAE19C981EEA8EE4003B95FE /* LCCKRecordAudioHUD.m in Sources */, 9AC44E951E15154300619112 /* UIView+WebCacheOperation.m in Sources */, 9AC44E511E15154300619112 /* POPAnimationRuntime.mm in Sources */, 9AC44D9C1E15154300619112 /* FDTransformLayer.m in Sources */, diff --git a/ChatKit/Class/Module/Conversation/View/ChatBar/LCCKChatBar.m b/ChatKit/Class/Module/Conversation/View/ChatBar/LCCKChatBar.m old mode 100644 new mode 100755 index 57a4bb04..171ea0fc --- a/ChatKit/Class/Module/Conversation/View/ChatBar/LCCKChatBar.m +++ b/ChatKit/Class/Module/Conversation/View/ChatBar/LCCKChatBar.m @@ -9,13 +9,13 @@ #import "LCCKChatBar.h" #import "LCCKChatMoreView.h" #import "LCCKChatFaceView.h" -#import "LCCKProgressHUD.h" +#import "LCCKRecordAudioHUD.h" #import "Mp3Recorder.h" -#if __has_include() -#import -#else -#import "Masonry.h" -#endif + #if __has_include() + #import + #else + #import "Masonry.h" + #endif #import "LCCKUIService.h" #import "UIImage+LCCKExtension.h" #import "NSString+LCCKExtension.h" @@ -333,25 +333,6 @@ - (void)textViewDidChange:(UITextView *)textView }); } -#pragma mark - MP3RecordedDelegate - -- (void)endConvertWithMP3FileName:(NSString *)fileName { - if (fileName) { - [LCCKProgressHUD dismissWithProgressState:LCCKProgressSuccess]; - [self sendVoiceMessage:fileName seconds:[LCCKProgressHUD seconds]]; - } else { - [LCCKProgressHUD dismissWithProgressState:LCCKProgressError]; - } -} - -- (void)failRecord { - [LCCKProgressHUD dismissWithProgressState:LCCKProgressError]; -} - -- (void)beginConvert { - [LCCKProgressHUD changeSubTitle:@"正在转换..."]; -} - #pragma mark - LCCKChatFaceViewDelegate - (void)faceViewSendFace:(NSString *)faceName { @@ -422,7 +403,66 @@ - (void)beginInputing { [self.textView becomeFirstResponder]; } +#pragma mark - MP3RecordedDelegate +- (void)averagePowerWithVolume:(CGFloat)volume { + [LCCKRecordAudioHUD changeVolume:volume]; +} + +- (void)endConvertWithMP3FileName:(NSString *)fileName { + if (fileName) { + [LCCKRecordAudioHUD dismissWithState:LCCKRecoderResultStateSuccess]; + [self sendVoiceMessage:fileName seconds:[LCCKRecordAudioHUD seconds]]; + } else { + [LCCKRecordAudioHUD dismissWithState:LCCKRecoderResultStateFail]; + } +} + +- (void)tooShortFailRecord { + [LCCKRecordAudioHUD dismissWithState:LCCKRecoderResultStateShort]; +} + #pragma mark - Private Methods +/** + * 开始录音 + */ +- (void)startRecordVoice { + [LCCKRecordAudioHUD show]; + self.voiceRecordButton.selected = YES; + [self.MP3 startRecord]; +} + +/** + * 取消录音 + */ +- (void)cancelRecordVoice { + [LCCKRecordAudioHUD dismissWithState:LCCKRecoderResultStateCancel]; + self.voiceRecordButton.selected = NO; + [self.MP3 cancelRecord]; +} + +/** + * 录音结束 + */ +- (void)confirmRecordVoice { + self.voiceRecordButton.selected = NO; + [self.MP3 stopRecord]; +} + +/** + * 更新录音显示状态,手指向上滑动后提示松开取消录音 + */ +- (void)updateCancelRecordVoice { + [LCCKRecordAudioHUD changeProgressState:LCCKRecordProgressStateOutSide]; + [_voiceRecordButton setTitle:@"松开 取消" forState:UIControlStateSelected]; +} + +/** + * 更新录音状态,手指重新滑动到范围内,提示向上取消录音 + */ +- (void)updateContinueRecordVoice { + [LCCKRecordAudioHUD changeProgressState:LCCKRecordProgressStateInSide]; + [_voiceRecordButton setTitle:@"松开 结束" forState:UIControlStateSelected]; +} - (void)keyboardWillHide:(NSNotification *)notification { NSString *reason = [NSString stringWithFormat:@"🔴类名与方法名:%@(在第%@行),描述:%@", @(__PRETTY_FUNCTION__), @(__LINE__), @"Should update on main thread"]; @@ -496,47 +536,11 @@ - (void)setup { }]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(confirmRecordVoice) name:kLCCKRecordAudioTooLong object:nil]; self.backgroundColor = self.messageInputViewBackgroundColor; [self setupConstraints]; -} - -/** - * 开始录音 - */ -- (void)startRecordVoice { - [LCCKProgressHUD show]; - self.voiceRecordButton.highlighted = YES; - [self.MP3 startRecord]; -} - -/** - * 取消录音 - */ -- (void)cancelRecordVoice { - [LCCKProgressHUD dismissWithMessage:@"取消录音"]; - self.voiceRecordButton.highlighted = NO; - [self.MP3 cancelRecord]; -} - -/** - * 录音结束 - */ -- (void)confirmRecordVoice { - [self.MP3 stopRecord]; -} - -/** - * 更新录音显示状态,手指向上滑动后提示松开取消录音 - */ -- (void)updateCancelRecordVoice { - [LCCKProgressHUD changeSubTitle:@"松开取消录音"]; -} - -/** - * 更新录音状态,手指重新滑动到范围内,提示向上取消录音 - */ -- (void)updateContinueRecordVoice { - [LCCKProgressHUD changeSubTitle:@"向上滑动取消录音"]; + + [LCCKRecordAudioHUD instance]; } - (void)setShowType:(LCCKFunctionViewShowType)showType { @@ -650,7 +654,7 @@ - (void)showMoreView:(BOOL)show { - (void)showVoiceView:(BOOL)show { self.voiceButton.selected = show; - self.voiceRecordButton.selected = show; +// self.voiceRecordButton.selected = show; self.voiceRecordButton.hidden = !show; self.textView.hidden = !self.voiceRecordButton.hidden; } @@ -773,10 +777,10 @@ - (UIButton *)voiceRecordButton { UIImage *voiceRecordButtonNormalBackgroundImage = [[self imageInBundlePathForImageName:@"VoiceBtn_Black"] resizableImageWithCapInsets:edgeInsets resizingMode:UIImageResizingModeStretch]; UIImage *voiceRecordButtonHighlightedBackgroundImage = [[self imageInBundlePathForImageName:@"VoiceBtn_BlackHL"] resizableImageWithCapInsets:edgeInsets resizingMode:UIImageResizingModeStretch]; [_voiceRecordButton setBackgroundImage:voiceRecordButtonNormalBackgroundImage forState:UIControlStateNormal]; - [_voiceRecordButton setBackgroundImage:voiceRecordButtonHighlightedBackgroundImage forState:UIControlStateHighlighted]; + [_voiceRecordButton setBackgroundImage:voiceRecordButtonHighlightedBackgroundImage forState:UIControlStateSelected]; _voiceRecordButton.titleLabel.font = [UIFont systemFontOfSize:14.0f]; [_voiceRecordButton setTitle:@"按住 说话" forState:UIControlStateNormal]; - [_voiceRecordButton setTitle:@"松开 结束" forState:UIControlStateHighlighted]; + [_voiceRecordButton setTitle:@"松开 结束" forState:UIControlStateSelected]; [_voiceRecordButton addTarget:self action:@selector(startRecordVoice) forControlEvents:UIControlEventTouchDown]; [_voiceRecordButton addTarget:self action:@selector(cancelRecordVoice) forControlEvents:UIControlEventTouchUpOutside]; [_voiceRecordButton addTarget:self action:@selector(confirmRecordVoice) forControlEvents:UIControlEventTouchUpInside]; diff --git a/ChatKit/Class/Module/Conversation/View/ChatBar/LCCKRecordAudioHUD.h b/ChatKit/Class/Module/Conversation/View/ChatBar/LCCKRecordAudioHUD.h new file mode 100644 index 00000000..38cb330f --- /dev/null +++ b/ChatKit/Class/Module/Conversation/View/ChatBar/LCCKRecordAudioHUD.h @@ -0,0 +1,91 @@ +// +// LCCKRecordAudioHUD.h +// LCCKChatBarExample +// +// Created by alex-W on 2017/6/7. +// Copyright © 2017年 http://codewpf.com/ . All rights reserved. +// + +#import + +#define kLCCKRecordAudioTooLong @"kLCCKRecordAudioTooLong" + +/** + 音频记录结果 + + - LCCKRecoderResultStateSuccess: 成功 + - LCCKRecoderResultStateFail: 失败 转换音频格式错误 + - LCCKRecoderResultStateShort: 失败 音频时长太短不发送 + - LCCKRecoderResultStateLong: 成功 音频时长太长结束并发送 + - LCCKRecoderResultStateCancel: 取消 + */ +typedef NS_ENUM(NSUInteger, LCCKRecordResultState) { + LCCKRecoderResultStateSuccess , + LCCKRecoderResultStateFail , + LCCKRecoderResultStateShort , + LCCKRecoderResultStateLong , + LCCKRecoderResultStateCancel , +}; + + +/** + 音频记录状态 + + - LCCKRecordProgressStateOutSide: 手指松开,取消发送 + - LCCKRecordProgressStateInSide: 手指上滑,取消发送 + */ +typedef NS_ENUM(NSUInteger, LCCKRecordProgressState) { + LCCKRecordProgressStateOutSide, + LCCKRecordProgressStateInSide, +}; + +@interface LCCKRecordAudioHUD : UIView +/** 是否正在显示 */ +@property (nonatomic, assign, readonly, getter=isShowing) BOOL showing; +/** + HUD 单例 + + @return 单例 + */ ++ (LCCKRecordAudioHUD *)instance; + +/** + 显示录音指示器 + */ ++ (void)show; + +/** + 隐藏录音指示器,根据结果 + + @param recderState 结果 + */ ++ (void)dismissWithState:(LCCKRecordResultState)resultState; + + +/** + 修改录音指示器,根据状态 + + @param progressState 状态 + */ ++ (void)changeProgressState:(LCCKRecordProgressState)progressState; + + +/** + 修改音量 + + @param level 音量 + */ ++ (void)changeVolume:(NSInteger)level; + + +/** + 录音总时间 + + @return 录音总时间 + */ ++ (NSInteger)seconds; + +@end + + + diff --git a/ChatKit/Class/Module/Conversation/View/ChatBar/LCCKRecordAudioHUD.m b/ChatKit/Class/Module/Conversation/View/ChatBar/LCCKRecordAudioHUD.m new file mode 100644 index 00000000..1b4beac7 --- /dev/null +++ b/ChatKit/Class/Module/Conversation/View/ChatBar/LCCKRecordAudioHUD.m @@ -0,0 +1,360 @@ +// +// LCCKRecordAudioHUD.m +// LCCKChatBarExample +// +// Created by alex-W on 2017/6/7. +// Copyright © 2017年 http://codewpf.com/ . All rights reserved. +// + +#import "LCCKRecordAudioHUD.h" +#import "UIImage+LCCKExtension.h" +#import "UIColor+LCCKExtension.h" +#if __has_include() +#import +#else +#import "Masonry.h" +#endif + +// 录音总时间 +const NSInteger LCCKRecordMAXTime = 60; +// Title 文字颜色 +NSString *const LCCKCancelStateTextColor = @"e5e5e5ff"; +NSString *const LCCKNormalStateTextColor = @"ffffffff"; + +// 结束 提示文字 +NSString *const LCCKRecoderResultStateFailTitle = @"转换音频格式失败"; +NSString *const LCCKRecoderResultStateShortTitle = @"说话时间太短"; +NSString *const LCCKRecoderResultStateLongTitle = @"说话时间超长"; +// 状态 提示文字 +NSString *const LCCKRecordProgressStateOutSideTitle = @"松开手指,取消发送"; +NSString *const LCCKRecordProgressStateInSideTitle = @"手指上滑,取消发送"; + + + +@interface LCCKRecordAudioHUD () +@property (nonatomic, assign) NSInteger seconds; +@property (nonatomic, assign) LCCKRecordResultState resultState; +@property (nonatomic, assign) LCCKRecordProgressState progressState; + +/** 倒计时 */ +@property (nonatomic, strong) NSTimer *timer; + +/** 整体控件 黑色透明背景图片 */ +@property (nonatomic, strong) UIImageView *bgIV; + +/** 提示标题 背景图片 */ +@property (nonatomic, strong) UIImageView *titleIV; +/** 提示标题 文本 */ +@property (nonatomic, strong) UILabel *titleLabel; + +/** 倒计时 文本 */ +@property (nonatomic, strong) UILabel *countDownLabel; +/** 警告 图片 */ +@property (nonatomic, strong) UIImageView *warningIV; +/** 取消 图片 */ +@property (nonatomic, strong) UIImageView *cancelIV; + +/** 正常录音 背景 */ +@property (nonatomic, strong) UIView *recordBg; +/** 正常录音 麦克风图片 */ +@property (nonatomic, strong) UIImageView *microPhoneIV; +/** 正常录音 音量图片 */ +@property (nonatomic, strong) UIImageView *volumeLevelIV; + +@end + + +@implementation LCCKRecordAudioHUD +#pragma mark - Init +- (instancetype)initWithFrame:(CGRect)frame { + if(self = [super initWithFrame:frame]) { + [self setup]; + } + return self; +} + +- (void)setup { + // 只需要初始化子控件 + [self titleIV]; + [self titleLabel]; + [self countDownLabel]; + [self warningIV]; + [self cancelIV]; + [self microPhoneIV]; + [self volumeLevelIV]; + + [self renew]; +} + +#pragma mark - Private Mehods +- (void)renew { + self.seconds = 0; + self.recordBg.hidden = NO; + self.warningIV.hidden = YES; + self.cancelIV.hidden = YES; + self.titleIV.hidden = YES; + self.countDownLabel.hidden = YES; + self.titleLabel.text = LCCKRecordProgressStateInSideTitle; + self.countDownLabel.text = [NSString stringWithFormat:@"%ld",LCCKRecordMAXTime-self.seconds]; +} +- (void)show { [self timer]; + + UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow; + [keyWindow addSubview:self]; + _showing = YES; +} + +- (void)dismissWithDelay:(BOOL)delay { + if(!_timer) return; + _showing = NO; + NSInteger delayTime = 0; + if(delay) { + delayTime = 1.5; + } + if(_timer) { + [_timer invalidate]; + _timer = nil; + } + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + if(self.superview) { + [self removeFromSuperview]; + [self renew]; + } + }); + +} +- (void)timerAction { + self.seconds++; + self.countDownLabel.text = [NSString stringWithFormat:@"%ld",LCCKRecordMAXTime-self.seconds]; + + if(LCCKRecordMAXTime-self.seconds < 10 && self.cancelIV.hidden == YES) { + self.countDownLabel.hidden = NO; + self.recordBg.hidden = YES; + } + + if(self.seconds == LCCKRecordMAXTime) { + [self setResultState:LCCKRecoderResultStateLong]; + [[NSNotificationCenter defaultCenter] postNotificationName:kLCCKRecordAudioTooLong object:nil]; + } + +} +#pragma mark - Setters +- (void)setResultState:(LCCKRecordResultState)resultState { + if(self.isShowing == NO) return; + + if(resultState == LCCKRecoderResultStateSuccess || + resultState == LCCKRecoderResultStateCancel) { + [self dismissWithDelay:NO]; + } else { + self.recordBg.hidden = YES; + self.cancelIV.hidden = YES; + self.countDownLabel.hidden = YES; + self.warningIV.hidden = NO; + if(resultState == LCCKRecoderResultStateFail) { + self.titleLabel.text = LCCKRecoderResultStateFailTitle; + } else if(resultState == LCCKRecoderResultStateShort){ + self.titleLabel.text = LCCKRecoderResultStateShortTitle; + } else if(resultState == LCCKRecoderResultStateLong) { + self.titleLabel.text = LCCKRecoderResultStateLongTitle; + } + [self dismissWithDelay:YES]; + } +} + +- (void)setProgressState:(LCCKRecordProgressState)progressState { + if(self.isShowing == NO) return; + + switch (progressState) { + case LCCKRecordProgressStateOutSide: + self.titleLabel.text = LCCKRecordProgressStateOutSideTitle; + self.titleIV.hidden = NO; + self.cancelIV.hidden = NO; + self.recordBg.hidden = YES; + self.countDownLabel.hidden = YES; + self.titleLabel.textColor = [UIColor CJ_16_Color:LCCKCancelStateTextColor]; + break; + case LCCKRecordProgressStateInSide: + self.titleLabel.text = LCCKRecordProgressStateInSideTitle; + self.titleIV.hidden = YES; + self.cancelIV.hidden = YES; + if(self.seconds > LCCKRecordMAXTime - 10) { + self.countDownLabel.hidden = NO; + } else { + self.recordBg.hidden = NO; + } + self.titleLabel.textColor = [UIColor CJ_16_Color:LCCKNormalStateTextColor]; + break; + } +} + + +#pragma mark - Getters +- (NSTimer *)timer { + if(_timer) { + [_timer invalidate]; + _timer = nil; + } + _timer = [NSTimer scheduledTimerWithTimeInterval:1 + target:self + selector:@selector(timerAction) + userInfo:nil + repeats:YES]; + [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]; + return _timer; +} + +- (UIImageView *)bgIV { + if(!_bgIV) { + _bgIV = [UIImageView new]; + _bgIV.image = [self imageInBundlePathForImageName:@"chat_record_allbg"]; + [_bgIV sizeToFit]; + [self addSubview:_bgIV]; + [_bgIV mas_makeConstraints:^(MASConstraintMaker *make) { + make.center.equalTo(self); + }]; + } + return _bgIV; +} + +- (UIImageView *)titleIV { + if(!_titleIV) { + _titleIV = [[UIImageView alloc] initWithFrame:CGRectMake(7.5f, 117, 135, 25)]; + _titleIV.image = [self imageInBundlePathForImageName:@"chat_record_titlebg"]; + _titleIV.hidden = YES; + [self.bgIV addSubview:_titleIV]; + } + return _titleIV; +} +- (UILabel *)titleLabel { + if(!_titleLabel) { + _titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(7.5f, 117, 135, 25)]; + _titleLabel.textAlignment = NSTextAlignmentCenter; + _titleLabel.font = [UIFont systemFontOfSize:14]; + _titleLabel.textColor = [UIColor CJ_16_Color:LCCKNormalStateTextColor]; + [self.bgIV addSubview:_titleLabel]; + } + return _titleLabel; +} +- (UILabel *)countDownLabel { + if(!_countDownLabel){ + _countDownLabel = [[UILabel alloc] initWithFrame:CGRectMake(25, 16, 100, 100)]; + _countDownLabel.textAlignment = NSTextAlignmentCenter; + _countDownLabel.font = [UIFont systemFontOfSize:80]; + _countDownLabel.textColor = [UIColor CJ_16_Color:LCCKNormalStateTextColor]; + _countDownLabel.hidden = YES; + [self.bgIV addSubview:_countDownLabel]; + } + return _countDownLabel; +} + +- (UIImageView *)warningIV { + if(!_warningIV) { + _warningIV = [[UIImageView alloc] initWithFrame:CGRectMake(71.5, 41.5f, 7, 55)]; + _warningIV.image = [self imageInBundlePathForImageName:@"chat_record_warning"]; + _warningIV.hidden = YES; + [self.bgIV addSubview:_warningIV]; + } + return _warningIV; +} + +- (UIImageView *)cancelIV { + if(!_cancelIV) { + _cancelIV = [[UIImageView alloc] initWithFrame:CGRectMake(49, 41, 52, 52)]; + _cancelIV.image = [self imageInBundlePathForImageName:@"chat_record_cancel"]; + _cancelIV.hidden = YES; + [self.bgIV addSubview:_cancelIV]; + } + return _cancelIV; +} + +- (UIView *)recordBg { + if(!_recordBg) { + _recordBg = [[UIView alloc] initWithFrame:CGRectMake(42, 30, 66, 66)]; + _recordBg.backgroundColor = [UIColor clearColor]; + _recordBg.hidden = YES; + [self.bgIV addSubview:_recordBg]; + } + return _recordBg; +} + +- (UIImageView *)microPhoneIV { + if(!_microPhoneIV) { + _microPhoneIV = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 39, 66)]; + _microPhoneIV.image = [self imageInBundlePathForImageName:@"chat_record_microphone"]; + [self.recordBg addSubview:_microPhoneIV]; + } + return _microPhoneIV; +} + +- (UIImageView *)volumeLevelIV { + if(!_volumeLevelIV) { + _volumeLevelIV = [[UIImageView alloc] initWithFrame:CGRectMake(48, 15, 18, 51)]; + _volumeLevelIV.image = [self imageInBundlePathForImageName:@"chat_record_volume_0"]; + [self.recordBg addSubview:_volumeLevelIV]; + } + return _volumeLevelIV; +} + +- (UIImage *)imageInBundlePathForImageName:(NSString *)imageName { + UIImage *image = [UIImage lcck_imageNamed:imageName bundleName:@"LCCKRecordAudioHUD" bundleForClass:[self class]]; + return image; +} + +#pragma mark - Class Methods ++ (LCCKRecordAudioHUD *)instance { + static dispatch_once_t onceToken; + static LCCKRecordAudioHUD *_instace; + dispatch_once(&onceToken, ^{ + _instace = [[LCCKRecordAudioHUD alloc]initWithFrame:[[UIScreen mainScreen] bounds]]; + _instace.backgroundColor = [UIColor clearColor]; + + }); + return _instace; +} + +/** +显示录音指示器 +*/ ++ (void)show { + [[LCCKRecordAudioHUD instance] show]; +} + +/** + 隐藏录音指示器,根据结果 + + @param recderState 结果 + */ ++ (void)dismissWithState:(LCCKRecordResultState)resultState { + [[LCCKRecordAudioHUD instance] setResultState:resultState]; +} + +/** + 修改录音指示器,根据状态 + + @param progressState 状态 + */ ++ (void)changeProgressState:(LCCKRecordProgressState)progressState { + [[LCCKRecordAudioHUD instance] setProgressState:progressState]; +} + +/** + 修改音量 + + @param level 音量 0 - 120 + */ ++ (void)changeVolume:(NSInteger)level { + // 转换为 0~8 + NSInteger l = level/10 - 2; + if(l < 0) l = 0; + if(l > 8) l = 8; + [LCCKRecordAudioHUD instance].volumeLevelIV.image = [[LCCKRecordAudioHUD instance] imageInBundlePathForImageName:[NSString stringWithFormat:@"chat_record_volume_%ld",l]]; +} + ++ (NSInteger)seconds{ + return [[LCCKRecordAudioHUD instance] seconds]; +} + + + + +@end diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_allbg@2x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_allbg@2x.png new file mode 100644 index 00000000..c334ca5c Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_allbg@2x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_allbg@3x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_allbg@3x.png new file mode 100644 index 00000000..54003473 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_allbg@3x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_cancel@2x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_cancel@2x.png new file mode 100644 index 00000000..440d9123 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_cancel@2x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_cancel@3x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_cancel@3x.png new file mode 100644 index 00000000..a112ce89 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_cancel@3x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_microphone@2x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_microphone@2x.png new file mode 100644 index 00000000..d28cbcc6 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_microphone@2x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_microphone@3x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_microphone@3x.png new file mode 100644 index 00000000..07fefbc0 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_microphone@3x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_titlebg@2x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_titlebg@2x.png new file mode 100644 index 00000000..ffe9fc8f Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_titlebg@2x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_titlebg@3x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_titlebg@3x.png new file mode 100644 index 00000000..b64f45b4 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_titlebg@3x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_0@2x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_0@2x.png new file mode 100644 index 00000000..e88910b9 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_0@2x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_0@3x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_0@3x.png new file mode 100644 index 00000000..e70e16ae Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_0@3x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_1@2x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_1@2x.png new file mode 100644 index 00000000..32919215 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_1@2x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_1@3x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_1@3x.png new file mode 100644 index 00000000..94251ab5 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_1@3x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_2@2x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_2@2x.png new file mode 100644 index 00000000..786c52d4 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_2@2x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_2@3x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_2@3x.png new file mode 100644 index 00000000..36e2e635 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_2@3x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_3@2x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_3@2x.png new file mode 100644 index 00000000..728c7391 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_3@2x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_3@3x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_3@3x.png new file mode 100644 index 00000000..e59ae557 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_3@3x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_4@2x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_4@2x.png new file mode 100644 index 00000000..52a7e0d9 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_4@2x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_4@3x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_4@3x.png new file mode 100644 index 00000000..12f50ef7 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_4@3x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_5@2x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_5@2x.png new file mode 100644 index 00000000..d3c1cd25 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_5@2x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_5@3x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_5@3x.png new file mode 100644 index 00000000..dcb1c7ed Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_5@3x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_6@2x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_6@2x.png new file mode 100644 index 00000000..7e6c946a Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_6@2x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_6@3x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_6@3x.png new file mode 100644 index 00000000..69859c28 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_6@3x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_7@2x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_7@2x.png new file mode 100644 index 00000000..1d81b766 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_7@2x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_7@3x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_7@3x.png new file mode 100644 index 00000000..63dff1d5 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_7@3x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_8@2x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_8@2x.png new file mode 100644 index 00000000..00dd51ba Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_8@2x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_8@3x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_8@3x.png new file mode 100644 index 00000000..996c6bd4 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_volume_8@3x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_warning@2x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_warning@2x.png new file mode 100644 index 00000000..3162af25 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_warning@2x.png differ diff --git a/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_warning@3x.png b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_warning@3x.png new file mode 100644 index 00000000..513cd254 Binary files /dev/null and b/ChatKit/Class/Resources/LCCKRecordAudioHUD.bundle/chat_record_warning@3x.png differ diff --git a/ChatKit/Class/Tool/Vendor/VoiceLib/Mp3Recorder.h b/ChatKit/Class/Tool/Vendor/VoiceLib/Mp3Recorder.h old mode 100755 new mode 100644 index 172a38a0..b9ddea1a --- a/ChatKit/Class/Tool/Vendor/VoiceLib/Mp3Recorder.h +++ b/ChatKit/Class/Tool/Vendor/VoiceLib/Mp3Recorder.h @@ -9,9 +9,24 @@ #import @protocol Mp3RecorderDelegate -- (void)failRecord; -- (void)beginConvert; +/** + 失败 时间太短 + */ +- (void)tooShortFailRecord; + +/** + 成功 转换格式成功 + @param fileName 文件路径 + */ - (void)endConvertWithMP3FileName:(NSString *)fileName; +/** + 音量 变化 + + @param volume 音量 + */ +- (void)averagePowerWithVolume:(CGFloat)volume; +@optional +- (void)beginConvert; @end @interface Mp3Recorder : NSObject diff --git a/ChatKit/Class/Tool/Vendor/VoiceLib/Mp3Recorder.m b/ChatKit/Class/Tool/Vendor/VoiceLib/Mp3Recorder.m index fd0ba562..1f57a6d7 100755 --- a/ChatKit/Class/Tool/Vendor/VoiceLib/Mp3Recorder.m +++ b/ChatKit/Class/Tool/Vendor/VoiceLib/Mp3Recorder.m @@ -18,6 +18,7 @@ @interface Mp3Recorder() @property (nonatomic, strong) AVAudioSession *session; @property (nonatomic, strong) AVAudioRecorder *recorder; +@property (nonatomic, strong) NSTimer *timer; @end @implementation Mp3Recorder @@ -53,6 +54,14 @@ - (void)setRecorder //NSLog(@"%@",recorderSetupError); } _recorder.meteringEnabled = YES; + if(_timer) { + [_timer invalidate]; + _timer = nil; + } + _timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(levelTimerCallback:) userInfo:nil repeats:YES]; + [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]; + + _recorder.delegate = self; [_recorder prepareToRecord]; } @@ -72,6 +81,8 @@ - (void)setSesstion - (void)startRecord { + if(_recorder && [_recorder isRecording]) return; + [self setSesstion]; [self setRecorder]; [_recorder record]; @@ -80,6 +91,8 @@ - (void)startRecord - (void)stopRecord { + if(!_recorder || ![_recorder isRecording] || !_timer) return; + double cTime = _recorder.currentTime; [_recorder stop]; @@ -88,17 +101,27 @@ - (void)stopRecord } else { [_recorder deleteRecording]; - - if ([_delegate respondsToSelector:@selector(failRecord)]) { - [_delegate failRecord]; + if(self.delegate && [self.delegate respondsToSelector:@selector(tooShortFailRecord)]) { + [self.delegate tooShortFailRecord]; } } + if(_timer) { + [_timer invalidate]; + _timer = nil; + } } - (void)cancelRecord { + if(!_recorder || ![_recorder isRecording] || !_timer) return; + [_recorder stop]; [_recorder deleteRecording]; + if(_timer) { + [_timer invalidate]; + _timer = nil; + } + } - (void)deleteMp3Cache @@ -127,9 +150,11 @@ - (void)audio_PCMtoMP3 NSString *mp3FilePath = [[self mp3Path] stringByAppendingPathComponent:[self randomMP3FileName]]; ////NSLog(@"MP3转换开始"); - if (_delegate && [_delegate respondsToSelector:@selector(beginConvert)]) { - [_delegate beginConvert]; + + if(self.delegate && [self.delegate respondsToSelector:@selector(beginConvert)]) { + [self.delegate beginConvert]; } + @try { int read, write; @@ -169,8 +194,8 @@ - (void)audio_PCMtoMP3 @finally { [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil]; //NSLog(@"MP3转换结束"); - if (_delegate && [_delegate respondsToSelector:@selector(endConvertWithMP3FileName:)]) { - [_delegate endConvertWithMP3FileName:mp3FilePath]; + if (self.delegate && [self.delegate respondsToSelector:@selector(endConvertWithMP3FileName:)]) { + [self.delegate endConvertWithMP3FileName:mp3FilePath]; } [self deleteCafCache]; } @@ -179,6 +204,45 @@ - (void)audio_PCMtoMP3 } #pragma mark - Path Utils + +/* 该方法确实会随环境音量变化而变化,但具体分贝值是否准确暂时没有研究 */ +- (void)levelTimerCallback:(NSTimer *)timer { + [self.recorder updateMeters]; + + float level; // The linear 0.0 .. 1.0 value we need. + float minDecibels = -80.0f; // Or use -60dB, which I measured in a silent room. + float decibels = [self.recorder averagePowerForChannel:0]; + + if (decibels < minDecibels) + { + level = 0.0f; + } + else if (decibels >= 0.0f) + { + level = 1.0f; + } + else + { + float root = 2.0f; + float minAmp = powf(10.0f, 0.05f * minDecibels); + float inverseAmpRange = 1.0f / (1.0f - minAmp); + float amp = powf(10.0f, 0.05f * decibels); + float adjAmp = (amp - minAmp) * inverseAmpRange; + + level = powf(adjAmp, 1.0f / root); + } + + /* + level 范围[0 ~ 1], 转为[0 ~120] 之间 + */ + + dispatch_async(dispatch_get_main_queue(), ^{ + if(self.delegate && [self.delegate respondsToSelector:@selector(averagePowerWithVolume:)]) { + [self.delegate averagePowerWithVolume:level*120]; + } + }); +} + - (NSString *)cafPath { NSString *cafPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"tmp.caf"]; return cafPath; @@ -198,4 +262,12 @@ - (NSString *)randomMP3FileName { return fileName; } +- (void)dealloc +{ + if(_timer) { + [_timer invalidate]; + _timer = nil; + } +} + @end