203 lines
5.6 KiB
Mathematica
203 lines
5.6 KiB
Mathematica
|
|
//
|
|||
|
|
// KBVoiceInputBar.m
|
|||
|
|
// keyBoard
|
|||
|
|
//
|
|||
|
|
// Created by Kiro on 2026/1/26.
|
|||
|
|
//
|
|||
|
|
|
|||
|
|
#import "KBVoiceInputBar.h"
|
|||
|
|
#import "KBAiRecordButton.h"
|
|||
|
|
#import <Masonry/Masonry.h>
|
|||
|
|
|
|||
|
|
@interface KBVoiceInputBar () <KBAiRecordButtonDelegate>
|
|||
|
|
|
|||
|
|
/// 毛玻璃背景容器
|
|||
|
|
@property (nonatomic, strong) UIView *backgroundView;
|
|||
|
|
|
|||
|
|
/// 毛玻璃效果
|
|||
|
|
@property (nonatomic, strong) UIVisualEffectView *blurEffectView;
|
|||
|
|
|
|||
|
|
/// 状态标签
|
|||
|
|
@property (nonatomic, strong) UILabel *statusLabel;
|
|||
|
|
|
|||
|
|
/// 录音按钮
|
|||
|
|
@property (nonatomic, strong) KBAiRecordButton *recordButton;
|
|||
|
|
|
|||
|
|
/// 是否正在录音
|
|||
|
|
@property (nonatomic, assign) BOOL isRecording;
|
|||
|
|
|
|||
|
|
@end
|
|||
|
|
|
|||
|
|
@implementation KBVoiceInputBar
|
|||
|
|
|
|||
|
|
#pragma mark - Lifecycle
|
|||
|
|
|
|||
|
|
- (instancetype)initWithFrame:(CGRect)frame {
|
|||
|
|
if (self = [super initWithFrame:frame]) {
|
|||
|
|
[self setupUI];
|
|||
|
|
}
|
|||
|
|
return self;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (instancetype)initWithCoder:(NSCoder *)coder {
|
|||
|
|
if (self = [super initWithCoder:coder]) {
|
|||
|
|
[self setupUI];
|
|||
|
|
}
|
|||
|
|
return self;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#pragma mark - 1:控件初始化
|
|||
|
|
|
|||
|
|
- (void)setupUI {
|
|||
|
|
self.backgroundColor = [UIColor clearColor];
|
|||
|
|
self.enabled = YES;
|
|||
|
|
self.isRecording = NO;
|
|||
|
|
|
|||
|
|
// 毛玻璃背景容器
|
|||
|
|
[self addSubview:self.backgroundView];
|
|||
|
|
[self.backgroundView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|||
|
|
make.edges.equalTo(self);
|
|||
|
|
}];
|
|||
|
|
|
|||
|
|
// 毛玻璃效果
|
|||
|
|
[self.backgroundView addSubview:self.blurEffectView];
|
|||
|
|
[self.blurEffectView mas_makeConstraints:^(MASConstraintMaker *make) {
|
|||
|
|
make.edges.equalTo(self.backgroundView);
|
|||
|
|
}];
|
|||
|
|
|
|||
|
|
// 为 blurEffectView 创建透明度渐变 mask
|
|||
|
|
CAGradientLayer *maskLayer = [CAGradientLayer layer];
|
|||
|
|
maskLayer.startPoint = CGPointMake(0.5, 1); // 底部
|
|||
|
|
maskLayer.endPoint = CGPointMake(0.5, 0); // 顶部
|
|||
|
|
maskLayer.colors = @[
|
|||
|
|
(__bridge id)[UIColor whiteColor].CGColor, // 底部:完全不透明
|
|||
|
|
(__bridge id)[UIColor whiteColor].CGColor, // 中间:完全不透明
|
|||
|
|
(__bridge id)[UIColor clearColor].CGColor // 顶部:完全透明
|
|||
|
|
];
|
|||
|
|
maskLayer.locations = @[@(0.0), @(0.5), @(1.0)];
|
|||
|
|
self.blurEffectView.layer.mask = maskLayer;
|
|||
|
|
|
|||
|
|
// 状态标签
|
|||
|
|
[self addSubview:self.statusLabel];
|
|||
|
|
[self.statusLabel mas_makeConstraints:^(MASConstraintMaker *make) {
|
|||
|
|
make.top.equalTo(self).offset(16);
|
|||
|
|
make.left.equalTo(self).offset(20);
|
|||
|
|
make.right.equalTo(self).offset(-20);
|
|||
|
|
make.height.mas_equalTo(20);
|
|||
|
|
}];
|
|||
|
|
|
|||
|
|
// 录音按钮
|
|||
|
|
[self addSubview:self.recordButton];
|
|||
|
|
[self.recordButton mas_makeConstraints:^(MASConstraintMaker *make) {
|
|||
|
|
make.top.equalTo(self.statusLabel.mas_bottom).offset(12);
|
|||
|
|
make.left.equalTo(self).offset(20);
|
|||
|
|
make.right.equalTo(self).offset(-20);
|
|||
|
|
make.height.mas_equalTo(50);
|
|||
|
|
make.bottom.lessThanOrEqualTo(self).offset(-16);
|
|||
|
|
}];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)layoutSubviews {
|
|||
|
|
[super layoutSubviews];
|
|||
|
|
|
|||
|
|
// 更新 mask 的 frame
|
|||
|
|
if (self.blurEffectView.layer.mask) {
|
|||
|
|
self.blurEffectView.layer.mask.frame = self.blurEffectView.bounds;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#pragma mark - Setter
|
|||
|
|
|
|||
|
|
- (void)setStatusText:(NSString *)statusText {
|
|||
|
|
_statusText = [statusText copy];
|
|||
|
|
self.statusLabel.text = statusText;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setEnabled:(BOOL)enabled {
|
|||
|
|
_enabled = enabled;
|
|||
|
|
self.recordButton.userInteractionEnabled = enabled;
|
|||
|
|
self.recordButton.alpha = enabled ? 1.0 : 0.5;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)setRecording:(BOOL)recording {
|
|||
|
|
_isRecording = recording;
|
|||
|
|
self.recordButton.state = recording ? KBAiRecordButtonStateRecording : KBAiRecordButtonStateNormal;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#pragma mark - Public Methods
|
|||
|
|
|
|||
|
|
- (void)updateVolumeRMS:(float)rms {
|
|||
|
|
[self.recordButton updateVolumeRMS:rms];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#pragma mark - KBAiRecordButtonDelegate
|
|||
|
|
|
|||
|
|
- (void)recordButtonDidBeginPress:(KBAiRecordButton *)button {
|
|||
|
|
if (!self.enabled) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
self.isRecording = YES;
|
|||
|
|
|
|||
|
|
if ([self.delegate respondsToSelector:@selector(voiceInputBarDidBeginRecording:)]) {
|
|||
|
|
[self.delegate voiceInputBarDidBeginRecording:self];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)recordButtonDidEndPress:(KBAiRecordButton *)button {
|
|||
|
|
self.isRecording = NO;
|
|||
|
|
|
|||
|
|
if ([self.delegate respondsToSelector:@selector(voiceInputBarDidEndRecording:)]) {
|
|||
|
|
[self.delegate voiceInputBarDidEndRecording:self];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (void)recordButtonDidCancelPress:(KBAiRecordButton *)button {
|
|||
|
|
self.isRecording = NO;
|
|||
|
|
|
|||
|
|
if ([self.delegate respondsToSelector:@selector(voiceInputBarDidCancelRecording:)]) {
|
|||
|
|
[self.delegate voiceInputBarDidCancelRecording:self];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#pragma mark - Lazy Load
|
|||
|
|
|
|||
|
|
- (UIView *)backgroundView {
|
|||
|
|
if (!_backgroundView) {
|
|||
|
|
_backgroundView = [[UIView alloc] init];
|
|||
|
|
_backgroundView.clipsToBounds = YES;
|
|||
|
|
}
|
|||
|
|
return _backgroundView;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (UIVisualEffectView *)blurEffectView {
|
|||
|
|
if (!_blurEffectView) {
|
|||
|
|
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight];
|
|||
|
|
_blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
|
|||
|
|
}
|
|||
|
|
return _blurEffectView;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (UILabel *)statusLabel {
|
|||
|
|
if (!_statusLabel) {
|
|||
|
|
_statusLabel = [[UILabel alloc] init];
|
|||
|
|
_statusLabel.text = @"按住按钮开始对话";
|
|||
|
|
_statusLabel.font = [UIFont systemFontOfSize:14];
|
|||
|
|
_statusLabel.textColor = [UIColor secondaryLabelColor];
|
|||
|
|
_statusLabel.textAlignment = NSTextAlignmentCenter;
|
|||
|
|
}
|
|||
|
|
return _statusLabel;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- (KBAiRecordButton *)recordButton {
|
|||
|
|
if (!_recordButton) {
|
|||
|
|
_recordButton = [[KBAiRecordButton alloc] init];
|
|||
|
|
_recordButton.delegate = self;
|
|||
|
|
_recordButton.normalTitle = @"按住说话";
|
|||
|
|
_recordButton.recordingTitle = @"松开结束";
|
|||
|
|
}
|
|||
|
|
return _recordButton;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@end
|