1
This commit is contained in:
@@ -25,6 +25,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
/// 波形条间距
|
/// 波形条间距
|
||||||
@property(nonatomic, assign) CGFloat barSpacing;
|
@property(nonatomic, assign) CGFloat barSpacing;
|
||||||
|
|
||||||
|
/// 自定义柱状高度基准(0~1),长度需 >= barCount,未设置则使用默认算法
|
||||||
|
@property(nonatomic, strong, nullable) NSArray<NSNumber *> *barHeightPattern;
|
||||||
|
|
||||||
/// 更新音量值
|
/// 更新音量值
|
||||||
/// @param rms 音量 RMS 值 (0.0 - 1.0)
|
/// @param rms 音量 RMS 值 (0.0 - 1.0)
|
||||||
- (void)updateWithRMS:(float)rms;
|
- (void)updateWithRMS:(float)rms;
|
||||||
|
|||||||
@@ -74,6 +74,11 @@
|
|||||||
|
|
||||||
CGFloat x = startX + i * (self.barWidth + self.barSpacing);
|
CGFloat x = startX + i * (self.barWidth + self.barSpacing);
|
||||||
CGFloat height = minHeight;
|
CGFloat height = minHeight;
|
||||||
|
if (self.barHeightPattern.count > i) {
|
||||||
|
CGFloat base = [self.barHeightPattern[i] floatValue];
|
||||||
|
base = MIN(MAX(base, 0.15), 0.9);
|
||||||
|
height = MAX(minHeight, maxHeight * base);
|
||||||
|
}
|
||||||
CGFloat y = (maxHeight - height) / 2;
|
CGFloat y = (maxHeight - height) / 2;
|
||||||
|
|
||||||
barLayer.frame = CGRectMake(x, y, self.barWidth, height);
|
barLayer.frame = CGRectMake(x, y, self.barWidth, height);
|
||||||
@@ -141,9 +146,15 @@
|
|||||||
CGFloat phase = (CGFloat)i / self.barLayers.count * M_PI * 2;
|
CGFloat phase = (CGFloat)i / self.barLayers.count * M_PI * 2;
|
||||||
CGFloat wave = sin(time * 3 + phase) * 0.3 + 0.7; // 0.4 - 1.0
|
CGFloat wave = sin(time * 3 + phase) * 0.3 + 0.7; // 0.4 - 1.0
|
||||||
|
|
||||||
// 计算高度
|
CGFloat baseFactor = 0.2;
|
||||||
CGFloat heightFactor = rms * wave;
|
if (self.barHeightPattern.count > i) {
|
||||||
CGFloat height = minHeight + range * heightFactor;
|
baseFactor = [self.barHeightPattern[i] floatValue];
|
||||||
|
baseFactor = MIN(MAX(baseFactor, 0.15), 0.9);
|
||||||
|
}
|
||||||
|
// 计算高度:基准高度 + 随 RMS 波动
|
||||||
|
CGFloat dynamicFactor = rms * (0.35 + 0.15 * wave); // 0.35~0.5
|
||||||
|
CGFloat heightFactor = MIN(1.0, baseFactor + dynamicFactor * (1.0 - baseFactor));
|
||||||
|
CGFloat height = maxHeight * heightFactor;
|
||||||
height = MAX(minHeight, MIN(maxHeight, height));
|
height = MAX(minHeight, MIN(maxHeight, height));
|
||||||
|
|
||||||
// 更新位置
|
// 更新位置
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#import "KBVoiceInputBar.h"
|
#import "KBVoiceInputBar.h"
|
||||||
#import "KBAiRecordButton.h"
|
#import "KBAiRecordButton.h"
|
||||||
|
#import "KBAiWaveformView.h"
|
||||||
#import <Masonry/Masonry.h>
|
#import <Masonry/Masonry.h>
|
||||||
|
|
||||||
@interface KBVoiceInputBar () <KBAiRecordButtonDelegate>
|
@interface KBVoiceInputBar () <KBAiRecordButtonDelegate>
|
||||||
@@ -34,6 +35,8 @@
|
|||||||
/// 录音中视图
|
/// 录音中视图
|
||||||
@property (nonatomic, strong) UIView *recordingView;
|
@property (nonatomic, strong) UIView *recordingView;
|
||||||
@property (nonatomic, strong) UIImageView *recordingCenterIconView;
|
@property (nonatomic, strong) UIImageView *recordingCenterIconView;
|
||||||
|
@property (nonatomic, strong) KBAiWaveformView *leftWaveformView;
|
||||||
|
@property (nonatomic, strong) KBAiWaveformView *rightWaveformView;
|
||||||
|
|
||||||
/// 取消视图
|
/// 取消视图
|
||||||
@property (nonatomic, strong) UIView *cancelView;
|
@property (nonatomic, strong) UIView *cancelView;
|
||||||
@@ -146,6 +149,21 @@
|
|||||||
make.width.height.mas_equalTo(36);
|
make.width.height.mas_equalTo(36);
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
[self.recordingView addSubview:self.leftWaveformView];
|
||||||
|
[self.recordingView addSubview:self.rightWaveformView];
|
||||||
|
[self.leftWaveformView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.recordingCenterIconView);
|
||||||
|
make.right.equalTo(self.recordingCenterIconView.mas_left).offset(-16);
|
||||||
|
make.width.mas_equalTo(84);
|
||||||
|
make.height.mas_equalTo(34);
|
||||||
|
}];
|
||||||
|
[self.rightWaveformView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
|
make.centerY.equalTo(self.recordingCenterIconView);
|
||||||
|
make.left.equalTo(self.recordingCenterIconView.mas_right).offset(16);
|
||||||
|
make.width.mas_equalTo(84);
|
||||||
|
make.height.mas_equalTo(34);
|
||||||
|
}];
|
||||||
|
|
||||||
// 取消视图
|
// 取消视图
|
||||||
[self.inputContainer addSubview:self.cancelView];
|
[self.inputContainer addSubview:self.cancelView];
|
||||||
[self.cancelView mas_makeConstraints:^(MASConstraintMaker *make) {
|
[self.cancelView mas_makeConstraints:^(MASConstraintMaker *make) {
|
||||||
@@ -224,6 +242,11 @@
|
|||||||
if (!self.toggleIconButton.hidden) {
|
if (!self.toggleIconButton.hidden) {
|
||||||
[self.inputContainer bringSubviewToFront:self.toggleIconButton];
|
[self.inputContainer bringSubviewToFront:self.toggleIconButton];
|
||||||
}
|
}
|
||||||
|
if (inputState == KBVoiceInputBarStateRecording) {
|
||||||
|
[self startRecordingWaveAnimationIfNeeded];
|
||||||
|
} else {
|
||||||
|
[self stopRecordingWaveAnimation];
|
||||||
|
}
|
||||||
[self updateCenterTextIfNeeded];
|
[self updateCenterTextIfNeeded];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,6 +254,11 @@
|
|||||||
|
|
||||||
- (void)updateVolumeRMS:(float)rms {
|
- (void)updateVolumeRMS:(float)rms {
|
||||||
[self.recordButton updateVolumeRMS:rms];
|
[self.recordButton updateVolumeRMS:rms];
|
||||||
|
if (self.inputState == KBVoiceInputBarStateRecording) {
|
||||||
|
CGFloat safeRMS = MAX(rms, 0.6f);
|
||||||
|
[self.leftWaveformView updateWithRMS:safeRMS];
|
||||||
|
[self.rightWaveformView updateWithRMS:safeRMS];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - KBAiRecordButtonDelegate
|
#pragma mark - KBAiRecordButtonDelegate
|
||||||
@@ -370,6 +398,30 @@
|
|||||||
return _recordingCenterIconView;
|
return _recordingCenterIconView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (KBAiWaveformView *)leftWaveformView {
|
||||||
|
if (!_leftWaveformView) {
|
||||||
|
_leftWaveformView = [[KBAiWaveformView alloc] init];
|
||||||
|
_leftWaveformView.waveColor = [UIColor whiteColor];
|
||||||
|
_leftWaveformView.barCount = 7;
|
||||||
|
_leftWaveformView.barWidth = 3;
|
||||||
|
_leftWaveformView.barSpacing = 6;
|
||||||
|
_leftWaveformView.barHeightPattern = @[@0.35, @0.85, @0.5, @0.35, @0.9, @0.55, @0.35];
|
||||||
|
}
|
||||||
|
return _leftWaveformView;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (KBAiWaveformView *)rightWaveformView {
|
||||||
|
if (!_rightWaveformView) {
|
||||||
|
_rightWaveformView = [[KBAiWaveformView alloc] init];
|
||||||
|
_rightWaveformView.waveColor = [UIColor whiteColor];
|
||||||
|
_rightWaveformView.barCount = 7;
|
||||||
|
_rightWaveformView.barWidth = 3;
|
||||||
|
_rightWaveformView.barSpacing = 6;
|
||||||
|
_rightWaveformView.barHeightPattern = @[@0.35, @0.85, @0.5, @0.35, @0.9, @0.55, @0.35];
|
||||||
|
}
|
||||||
|
return _rightWaveformView;
|
||||||
|
}
|
||||||
|
|
||||||
- (UIView *)cancelView {
|
- (UIView *)cancelView {
|
||||||
if (!_cancelView) {
|
if (!_cancelView) {
|
||||||
_cancelView = [[UIView alloc] init];
|
_cancelView = [[UIView alloc] init];
|
||||||
@@ -478,4 +530,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - Recording Wave
|
||||||
|
|
||||||
|
- (void)startRecordingWaveAnimationIfNeeded {
|
||||||
|
[self.leftWaveformView startIdleAnimation];
|
||||||
|
[self.rightWaveformView startIdleAnimation];
|
||||||
|
[self.leftWaveformView updateWithRMS:0.7f];
|
||||||
|
[self.rightWaveformView updateWithRMS:0.7f];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)stopRecordingWaveAnimation {
|
||||||
|
[self.leftWaveformView stopAnimation];
|
||||||
|
[self.rightWaveformView stopAnimation];
|
||||||
|
[self.leftWaveformView reset];
|
||||||
|
[self.rightWaveformView reset];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
Reference in New Issue
Block a user