CWTalkBackView.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. //
  2. // CWTalkBackView.m
  3. // QQVoiceDemo
  4. //
  5. // Created by 陈旺 on 2017/10/4.
  6. // Copyright © 2017年 陈旺. All rights reserved.
  7. //
  8. #import "CWTalkBackView.h"
  9. #import "UIView+CWChat.h"
  10. #import "CWRecordStateView.h"
  11. #import "CWVoiceButton.h"
  12. #import "CWRecorder.h"
  13. #import "CWAudioPlayer.h"
  14. #import "CWAudioPlayView.h"
  15. #import "CWVoiceView.h"
  16. #import "CWFlieManager.h"
  17. //----------------------对讲---------------------------------//
  18. @interface CWTalkBackView ()<CWRecorderDelegate>
  19. @property (nonatomic, weak) CWRecordStateView *stateView;
  20. @property (nonatomic, weak) CWVoiceButton *micButton; // 录音按钮
  21. @property (nonatomic, weak) CWVoiceButton *playButton; // 播放按钮
  22. @property (nonatomic, weak) CWVoiceButton *cancelButton; // 取消按钮
  23. @property (nonatomic, weak) CWAudioPlayView *playView; // 播放界面
  24. @property (nonatomic, weak) UIImageView *voiceLine; // aio_voice_line
  25. @end
  26. static CGFloat const maxScale = 0.45;
  27. @implementation CWTalkBackView
  28. - (instancetype)initWithFrame:(CGRect)frame
  29. {
  30. self = [super initWithFrame:frame];
  31. if (self) {
  32. // self.backgroundColor = [UIColor greenColor];
  33. [self setupSubViews];
  34. }
  35. return self;
  36. }
  37. - (void)setupSubViews {
  38. [self stateView]; // 创建当前状态的view
  39. [self voiceLine]; // 录音时的曲线
  40. [self micButton]; // 创建micPhone按钮
  41. [self playButton]; // 创建播放按钮
  42. [self cancelButton]; // 创建取消按钮
  43. [CWRecorder shareInstance].delegate = self;
  44. }
  45. #pragma mark - 创建subViews
  46. - (UIImageView *)voiceLine {
  47. if (_voiceLine == nil) {
  48. UIImageView *imageV = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"aio_voice_line"]];
  49. // imageV.cw_centerX = self.cw_width / 2.0;
  50. imageV.hidden = YES;
  51. // imageV.backgroundColor = [UIColor redColor];
  52. [self addSubview:imageV];
  53. _voiceLine = imageV;
  54. }
  55. return _voiceLine;
  56. }
  57. - (CWAudioPlayView *)playView {
  58. if (_playView == nil) {
  59. CWAudioPlayView *view = [[CWAudioPlayView alloc] initWithFrame:self.bounds];
  60. [self addSubview:view];
  61. _playView = view;
  62. }
  63. return _playView;
  64. }
  65. - (CWRecordStateView *)stateView {
  66. if (_stateView == nil) {
  67. CWRecordStateView *stateView = [[CWRecordStateView alloc] initWithFrame:CGRectMake(0, 10, self.cw_width, 50)];
  68. // stateView.backgroundColor = [UIColor blueColor];
  69. [self addSubview:stateView];
  70. _stateView = stateView;
  71. }
  72. return _stateView;
  73. }
  74. - (CWVoiceButton *)cancelButton {
  75. if (_cancelButton == nil) {
  76. CWVoiceButton *btn = [CWVoiceButton buttonWithBackImageNor:@"aio_voice_operate_nor" backImageSelected:@"aio_voice_operate_press" imageNor:@"aio_voice_operate_delete_nor" imageSelected:@"aio_voice_operate_delete_press" frame:CGRectMake(self.cw_width - 35 , self.stateView.cw_bottom + 10, 0, 0) isMicPhone:NO];
  77. btn.frame = CGRectMake(self.cw_width - 35 - btn.norImage.size.width, self.stateView.cw_bottom + 10, btn.norImage.size.width, btn.norImage.size.height);
  78. [self addSubview:btn];
  79. btn.hidden = YES;
  80. _cancelButton = btn;
  81. }
  82. return _cancelButton;
  83. }
  84. - (CWVoiceButton *)playButton {
  85. if (_playButton == nil) {
  86. CWVoiceButton *btn = [CWVoiceButton buttonWithBackImageNor:@"aio_voice_operate_nor" backImageSelected:@"aio_voice_operate_press" imageNor:@"aio_voice_operate_listen_nor" imageSelected:@"aio_voice_operate_listen_press" frame:CGRectMake(35, self.stateView.cw_bottom + 10, 0, 0) isMicPhone:NO];
  87. [self addSubview:btn];
  88. btn.hidden = YES;
  89. _playButton = btn;
  90. }
  91. return _playButton;
  92. }
  93. - (CWVoiceButton *)micButton {
  94. if (_micButton == nil) {
  95. CWVoiceButton *btn = [CWVoiceButton buttonWithBackImageNor:@"aio_voice_button_nor" backImageSelected:@"aio_voice_button_press" imageNor:@"aio_voice_button_icon" imageSelected:@"aio_voice_button_icon" frame:CGRectMake(0, self.stateView.cw_bottom, 0, 0) isMicPhone:YES];
  96. // 手指按下
  97. [btn addTarget:self action:@selector(starRecorde:) forControlEvents:UIControlEventTouchDown];
  98. // 松开手指
  99. [btn addTarget:self action:@selector(sendRecorde:) forControlEvents:UIControlEventTouchUpInside];
  100. // 拖动手势
  101. UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
  102. [btn addGestureRecognizer:pan];
  103. btn.cw_centerX = self.cw_width / 2.0;
  104. self.voiceLine.center = btn.center;
  105. [self addSubview:btn];
  106. _micButton = btn;
  107. }
  108. return _micButton;
  109. }
  110. #pragma mark - 拖拽手势
  111. - (void)pan:(UIPanGestureRecognizer *)pan {
  112. if (!self.micButton.isSelected) return;
  113. CGPoint point = [pan locationInView:pan.view.superview];
  114. if (pan.state == UIGestureRecognizerStateBegan) {
  115. // NSLog(@"began");
  116. }else if (pan.state == UIGestureRecognizerStateChanged) {
  117. __weak __typeof(self)weakSelf = self;
  118. if (point.x < self.cw_width / 2.0) { // 触摸在左边
  119. [self transitionButton:self.playButton WithPoint:point containBlock:^(BOOL isContain) {
  120. if (isContain) { //触摸到了播放按钮内
  121. weakSelf.stateView.recordState = CWRecordStateListen;
  122. }else {
  123. weakSelf.stateView.recordState = CWRecordStateRecording;
  124. }
  125. }];
  126. }else { // 触摸在右边
  127. // NSLog(@"%@",NSStringFromCGRect(self.cancelButton.backgroudLayer.frame));
  128. [self transitionButton:self.cancelButton WithPoint:point containBlock:^(BOOL isContain) {
  129. // NSLog(@"%zd=================",isContain);
  130. if (isContain) { //触摸到了播放按钮内
  131. weakSelf.stateView.recordState = CWRecordStateCancel;
  132. }else {
  133. weakSelf.stateView.recordState = CWRecordStateRecording;
  134. }
  135. }];
  136. }
  137. }else { // 松开手指 或者 手势cancel
  138. [[CWRecorder shareInstance] endRecord]; // 结束录音
  139. [self.stateView endRecord];
  140. if (self.stateView.recordState == CWRecordStateListen) {
  141. NSLog(ASLocalizedString(@"试听..."));
  142. self.playView = nil;
  143. [self playView];
  144. }else if (self.stateView.recordState == CWRecordStateCancel) {
  145. NSLog(ASLocalizedString(@"取消发送..."));
  146. [[CWRecorder shareInstance] deleteRecord];
  147. // 设置状态 显示小圆点和三个标签
  148. [(CWVoiceView *)self.superview.superview setState:CWVoiceStateDefault];
  149. }else {
  150. NSLog(ASLocalizedString(@"发送语音"));
  151. // 设置状态 显示小圆点和三个标签
  152. [(CWVoiceView *)self.superview.superview setState:CWVoiceStateDefault];
  153. }
  154. self.micButton.selected = NO;
  155. self.playButton.selected = NO;
  156. self.cancelButton.selected = NO;
  157. self.playButton.hidden = YES;
  158. self.cancelButton.hidden = YES;
  159. self.voiceLine.hidden = YES;
  160. self.playButton.backgroudLayer.transform = CATransform3DIdentity;
  161. self.cancelButton.backgroudLayer.transform = CATransform3DIdentity;
  162. self.stateView.recordState = CWRecordStateDefault;
  163. }
  164. }
  165. #pragma mark 按钮的形变以及动画
  166. - (void)transitionButton:(CWVoiceButton *)btn WithPoint:(CGPoint)point containBlock:(void(^)(BOOL isContain))block{
  167. CGFloat distance = [self distanceWithPointA:btn.center pointB:point];
  168. CGFloat d = btn.cw_width * 3 / 4;
  169. CGFloat x = distance * maxScale / d;
  170. CGFloat scale = 1 - x;
  171. scale = scale > 0 ? scale > maxScale ? maxScale : scale : 0;
  172. CGPoint p = [self.layer convertPoint:point toLayer:btn.backgroudLayer];
  173. if ([btn.backgroudLayer containsPoint:p]) {
  174. btn.selected = YES;
  175. btn.backgroudLayer.transform = CATransform3DMakeScale(1 + maxScale, 1 + maxScale, 1);
  176. if (block) {
  177. block(YES);
  178. }
  179. }else {
  180. btn.backgroudLayer.transform = CATransform3DMakeScale(1 + scale, 1 + scale, 1);
  181. btn.selected = NO;
  182. if (block) {
  183. block(NO);
  184. }
  185. }
  186. }
  187. #pragma mark 按钮的动画
  188. // 麦克风按钮动画
  189. - (void)animationMicBtn:(void(^)(BOOL finished))completion {
  190. [UIView animateWithDuration:0.10 animations:^{
  191. self.micButton.transform = CGAffineTransformMakeScale(1.1, 1.1);
  192. } completion:^(BOOL finished) {
  193. [UIView animateWithDuration:0.05 animations:^{
  194. self.micButton.transform = CGAffineTransformIdentity;
  195. } completion:^(BOOL finished) {
  196. if (completion) {
  197. completion(finished);
  198. }
  199. }];
  200. }];
  201. }
  202. // 播放与取消按钮动画
  203. - (void)animationPlayAndCancelBtn {
  204. [self animationWithStarPoint:CGPointMake(self.playButton.cw_centerX + 20, self.playButton.cw_centerY) endPoint:self.playButton.center view:self.playButton];
  205. [self animationWithStarPoint:CGPointMake(self.cancelButton.cw_centerX - 20, self.cancelButton.cw_centerY) endPoint:self.cancelButton.center view:self.cancelButton];
  206. }
  207. - (void)animationWithStarPoint:(CGPoint)starP endPoint:(CGPoint)endP view:(UIView *)view {
  208. view.hidden = NO;
  209. CABasicAnimation *positionAnim = [CABasicAnimation animationWithKeyPath:@"position"];
  210. positionAnim.fromValue = [NSValue valueWithCGPoint:starP];
  211. positionAnim.toValue = [NSValue valueWithCGPoint:endP];
  212. positionAnim.duration = 0.15;
  213. CABasicAnimation *opacityAnim = [CABasicAnimation animationWithKeyPath:@"opacity"];
  214. opacityAnim.toValue = @1;
  215. opacityAnim.fromValue = @0;
  216. CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
  217. animationGroup.animations = @[positionAnim,opacityAnim];
  218. animationGroup.duration = 0.15;
  219. [view.layer addAnimation:animationGroup forKey:nil];
  220. }
  221. // 曲线动画
  222. - (void)animationVoiceLine {
  223. self.voiceLine.transform = CGAffineTransformMakeScale(0.8, 0.8);
  224. self.voiceLine.hidden = NO;
  225. [UIView animateWithDuration:0.15 animations:^{
  226. self.voiceLine.transform = CGAffineTransformIdentity;
  227. }];
  228. }
  229. #pragma mark - 录音按钮 点击事件
  230. // 开始录音
  231. - (void)starRecorde:(UIButton *)btn {
  232. NSLog(ASLocalizedString(@"开始录音"));
  233. [CWRecorder shareInstance].delegate = self;
  234. btn.selected = YES;
  235. // 设置状态 隐藏小圆点和三个标签
  236. [(CWVoiceView *)self.superview.superview setState:CWVoiceStateRecord];
  237. [self animationMicBtn:^(BOOL finished) {
  238. // NSString *path = [CWDocumentPath stringByAppendingPathComponent:@"test.wav"];
  239. // @"/Users/chavez/Desktop/test.wav"
  240. NSString *filePath = [CWFlieManager filePath];
  241. [[CWRecorder shareInstance] beginRecordWithRecordPath:filePath];
  242. }];
  243. }
  244. // 手指松开 发送录音
  245. - (void)sendRecorde:(UIButton *)btn {
  246. NSTimeInterval t = 0;
  247. if (![CWRecorder shareInstance].isRecording) {
  248. t = 0.3;
  249. }
  250. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(t * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  251. btn.selected = NO;
  252. self.playButton.hidden = YES;
  253. self.cancelButton.hidden = YES;
  254. self.voiceLine.hidden = YES;
  255. self.stateView.recordState = CWRecordStateDefault;
  256. [[CWRecorder shareInstance] endRecord];
  257. [self.stateView endRecord];
  258. // 设置状态 显示小圆点和三个标签
  259. [(CWVoiceView *)self.superview.superview setState:CWVoiceStateDefault];
  260. if (t == 0) {
  261. NSLog(ASLocalizedString(@"发送录音111111"));
  262. }else {
  263. NSLog(ASLocalizedString(@"录音时间太短"));
  264. }
  265. });
  266. }
  267. // 计算两点之间的距离
  268. - (CGFloat)distanceWithPointA:(CGPoint)pointA pointB:(CGPoint)pointB {
  269. CGFloat distance = sqrt(pow((pointA.x - pointB.x), 2) + pow((pointA.y - pointB.y), 2));
  270. return distance;
  271. }
  272. #pragma mark - CWRecorderDelegate
  273. - (void)recorderPrepare {
  274. // NSLog(ASLocalizedString(@"准备中......"));
  275. self.stateView.recordState = CWRecordStatePrepare;
  276. }
  277. - (void)recorderRecording {
  278. self.stateView.recordState = CWRecordStateRecording;
  279. [self animationPlayAndCancelBtn]; // 播放按钮 和 取消按钮的动画
  280. [self animationVoiceLine]; // 曲线动画
  281. // 设置状态view开始录音
  282. [self.stateView beginRecord];
  283. }
  284. - (void)recorderFailed:(NSString *)failedMessage {
  285. self.stateView.recordState = CWRecordStateDefault;
  286. NSLog(ASLocalizedString(@"失败:%@"),failedMessage);
  287. }
  288. @end