前言
在社交媒体中开发中遇到需要做类似微信录音上滑取消功能,例子,虽然不是很好看,但是细节满满哦,另外参数可以自己重置,满足自己产品的需求。
效果
- 按住Record开始录音
- 向上取消录音并且cancel出现伴随下移动作
- 如果录音长度短于1秒就重新录音
代码
先自定义UIButton
- SwipVoiceButton.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol SwipVoiceButtonDelegate <NSObject>
@optional
- (void)swipVoiceButtonOffset:(CGFloat)offset; //上移偏移量
@end
static CGFloat const kOverRangeY = 50.0f;
@interface SwipVoiceButton : UIButton
@property(nonatomic, weak) id<SwipVoiceButtonDelegate> delegate;
@end
NS_ASSUME_NONNULL_END
- SwipVoiceButton.m
#import "SwipVoiceButton.h"
@implementation SwipVoiceButton
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint point = [touch locationInView:self];
CGFloat offset = 0;
if(point.y < -kOverRangeY) { //上移
offset = -kOverRangeY;
} else if(point.y > 0) {
offset = 0;
} else {
offset = point.y;
}
//NSLog(@"point%f", offset);
if(_delegate && [_delegate respondsToSelector:@selector(swipVoiceButtonOffset:)]) {
[_delegate swipVoiceButtonOffset:offset];
}
return [super continueTrackingWithTouch:touch withEvent:event];
}
@end
使用
- ViewController.m
#import "ViewController.h"
#import "SwipVoiceButton.h"
#import <AVFoundation/AVFoundation.h>
#define kForMaxTime 59
static NSString *const kMessageWAVMessageRadio = @"WAVMessageRadio";
static NSString *const kMessageAMRMessageRadio = @"AMRMessageRadio";
@interface ViewController () <SwipVoiceButtonDelegate>
@property(nonatomic, strong) SwipVoiceButton *clickVoiceButton;
@property(nonatomic, strong) AVAudioRecorder *audioRecorder;
@property(nonatomic, strong) NSTimer *timer;
@property(nonatomic, assign) NSInteger count;
@property(nonatomic, strong) UILabel *tipLabel;
@property(nonatomic, strong) UILabel *cancelLabel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setViewControllerUI];
}
- (void)setViewControllerUI {
[self.view addSubview:self.clickVoiceButton];
[self.view addSubview:self.tipLabel];
[self.view addSubview:self.cancelLabel];
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
self.clickVoiceButton.frame = CGRectMake((self.view.frame.size.width / 2) - 22, self.view.frame.size.height - 120, 44, 44);
self.tipLabel.frame = CGRectMake(0, (self.view.frame.size.height / 2) + 50, self.view.frame.size.width, 20);
self.cancelLabel.frame = CGRectMake(0, (self.view.frame.size.height / 2) - 50, self.view.frame.size.width, 20);
}
- (UILabel *)tipLabel {
if(_tipLabel == nil) {
_tipLabel = [[UILabel alloc]init];
_tipLabel.textColor = [UIColor blackColor];
_tipLabel.font = [UIFont systemFontOfSize:15];
_tipLabel.textAlignment = NSTextAlignmentCenter;
[_tipLabel sizeToFit];
}
return _tipLabel;
}
- (UILabel *)cancelLabel {
if(_cancelLabel == nil) {
_cancelLabel = [[UILabel alloc]init];
_cancelLabel.text = @"Cancel";
_cancelLabel.textColor = [UIColor blackColor];
_cancelLabel.font = [UIFont systemFontOfSize:15];
_cancelLabel.textAlignment = NSTextAlignmentCenter;
[_cancelLabel sizeToFit];
_cancelLabel.hidden = YES;
}
return _cancelLabel;
}
- (SwipVoiceButton *)clickVoiceButton {
if(_clickVoiceButton == nil) {
_clickVoiceButton = [[SwipVoiceButton alloc]init];
_clickVoiceButton.backgroundColor = [UIColor yellowColor];
_clickVoiceButton.layer.cornerRadius = 22;
_clickVoiceButton.layer.masksToBounds = YES;
[_clickVoiceButton setTitle:@"Record" forState:UIControlStateNormal];
_clickVoiceButton.titleLabel.font = [UIFont systemFontOfSize:10];
[_clickVoiceButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
_clickVoiceButton.delegate = self;
[_clickVoiceButton addTarget:self action:@selector(startRecordVoice) forControlEvents:UIControlEventTouchDown]; //开始录音
[_clickVoiceButton addTarget:self action:@selector(cancelRecordVoice) forControlEvents:UIControlEventTouchUpOutside]; //取消录音
[_clickVoiceButton addTarget:self action:@selector(endRecordVoice) forControlEvents:UIControlEventTouchUpInside]; //录音结束
[_clickVoiceButton addTarget:self action:@selector(upswipeCancelRecordVoice) forControlEvents:UIControlEventTouchDragExit]; //向上滑动 提示松开取消录音
[_clickVoiceButton addTarget:self action:@selector(downSwipeContinueRecordVoice) forControlEvents:UIControlEventTouchDragEnter]; //手指重新滑动到范围内 提示向上取消录音
}
return _clickVoiceButton;
}
- (AVAudioRecorder *)audioRecorder {
if (_audioRecorder == nil) {
NSString *wavRecordFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.wav",kMessageWAVMessageRadio]];//WAVMessageRadio
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
[audioSession setActive:YES error:nil];
NSDictionary *recordSetting = @{ AVSampleRateKey : @8000.0, // 采样率
AVFormatIDKey : @(kAudioFormatLinearPCM), // 音频格式
AVLinearPCMBitDepthKey : @16, // 采样位数 默认 16
AVNumberOfChannelsKey : @1 // 通道的数目
};
_audioRecorder = [[AVAudioRecorder alloc] initWithURL:[NSURL URLWithString:wavRecordFilePath] settings:recordSetting error:nil];
_audioRecorder.meteringEnabled = YES;
}
return _audioRecorder;
}
#pragma mark - 按钮方法
/*
0.录音
0.0 大于1秒
1.录制完毕
0.1 小于一秒重新录制
*/
- (void)startRecordVoice { //开始录音
NSLog(@"%@%s", NSStringFromClass([self class]), __func__);
//0.开始录音
self.tipLabel.text = @"开始录音";
//1.开始录音回调
[self _beginAudioRecorder];
//开启60s倒计时
[self startMaxTimeTask];
}
- (void)endRecordVoice { //录音结束
NSLog(@"%@%s", NSStringFromClass([self class]), __func__);
[self endMaxTimeTask];
//-1.间隔时间大于1s
if([self _canSendRecordVoice]) { //可以发送
//0.展示发送录音动画
self.tipLabel.text = @"录音完成发送录音 1s 后重置";
[self.tipLabel performSelector:@selector(setText:) withObject:@"开始录音" afterDelay:1.0];
} else { //不可以发送
//0.重新录音
self.tipLabel.text = @"重新录音";
self.cancelLabel.hidden = YES;
}
}
- (void)upswipeCancelRecordVoice { //向上滑动 提示松开取消录音
//NSLog(@"%@%s", NSStringFromClass([self class]), __func__);
self.tipLabel.text = @"松开取消录音";
}
- (void)cancelRecordVoice { //向上取消后
NSLog(@"%@%s", NSStringFromClass([self class]), __func__);
//0.重新录音
self.tipLabel.text = @"重新录音";
//1.录音取消删除回调
[self _deleteAudioRecorder];
//2.取消计时器
[self endMaxTimeTask];
}
- (void)downSwipeContinueRecordVoice { //手指重新滑动到范围内 提示向上取消录音
//NSLog(@"%@-%s", NSStringFromClass([self class]), __func__);
self.tipLabel.text = @"向上取消录音";
}
#pragma mark - 按钮方法 end
#pragma mark - MessageChatSwipVoiceButtonDelegate
- (void)swipVoiceButtonOffset:(CGFloat)offset { //上移偏移量 0~-50
NSLog(@"offset %f", offset);
if(fabs(offset) > 10) { //取消录音
self.cancelLabel.hidden = NO;
} else { //重回录音
self.cancelLabel.hidden = YES;
}
self.cancelLabel.center = CGPointMake(self.view.center.x, self.view.center.y - offset);
}
#pragma mark - MessageChatSwipVoiceButtonDelegate end
#pragma mark - audioRecorder方法
- (void)_beginAudioRecorder {
[self.audioRecorder prepareToRecord];
[self.audioRecorder record];
}
- (void)_deleteAudioRecorder {
NSString *wavRecordFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.wav",kMessageWAVMessageRadio]];//WAVMessageRadio
NSString *amrRecordFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.amr",kMessageAMRMessageRadio]];//AMRMessageRadio
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager removeItemAtPath:wavRecordFilePath error:nil];
[fileManager removeItemAtPath:amrRecordFilePath error:nil];
}
- (void)_stopAudioRecorder {
[self.audioRecorder stop];
//恢复外部音乐
[[AVAudioSession sharedInstance] setActive:NO
withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
error:nil];
}
#pragma mark - audioRecorder方法 end
#pragma mark - timer for max
- (void)startMaxTimeTask {
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(taskAction) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
_count = kForMaxTime;
}
- (void)endMaxTimeTask {
[_timer invalidate];
[_timer fire];
_timer = nil;
}
- (void)taskAction {
if(_count == 0) {
[self endRecordVoice];
[self endMaxTimeTask];
NSLog(@"end record %ld", self.count);
}
NSLog(@"begian record %ld", self.count);
_count --;
}
- (void)dealloc {
[self endMaxTimeTask];
}
#pragma mark - timer for max end
#pragma mark - private method
- (BOOL)_canSendRecordVoice { //是否大于1s可以发送
BOOL cansend = NO;
if(self.audioRecorder.currentTime < 1.0f) { //时间小于1秒
NSLog(@"Time too short!");
cansend = NO;
} else {
cansend = YES;
}
[self _stopAudioRecorder];
return cansend;
}
#pragma mark - private method end
@end
监听上下滑动代码
- (void)swipVoiceButtonOffset:(CGFloat)offset { //上移偏移量 0~-50
NSLog(@"offset %f", offset);
if(fabs(offset) > 10) { //取消录音
self.cancelLabel.hidden = NO;
} else { //重回录音
self.cancelLabel.hidden = YES;
}
self.cancelLabel.center = CGPointMake(self.view.center.x, self.view.center.y - offset);
}