// // KBAiWaveformView.m // keyBoard // // Created by Mac on 2026/1/15. // #import "KBAiWaveformView.h" @interface KBAiWaveformView () @property(nonatomic, strong) NSMutableArray *barLayers; @property(nonatomic, strong) NSMutableArray *barHeights; @property(nonatomic, strong) CADisplayLink *displayLink; @property(nonatomic, assign) float currentRMS; @property(nonatomic, assign) float targetRMS; @property(nonatomic, assign) BOOL isAnimating; @end @implementation KBAiWaveformView - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self setup]; } return self; } - (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { [self setup]; } return self; } - (void)setup { _waveColor = [UIColor systemBlueColor]; _barCount = 5; _barWidth = 4; _barSpacing = 3; _barLayers = [[NSMutableArray alloc] init]; _barHeights = [[NSMutableArray alloc] init]; _currentRMS = 0; _targetRMS = 0; self.backgroundColor = [UIColor clearColor]; } - (void)layoutSubviews { [super layoutSubviews]; [self setupBars]; } - (void)setupBars { // 移除旧的图层 for (CAShapeLayer *layer in self.barLayers) { [layer removeFromSuperlayer]; } [self.barLayers removeAllObjects]; [self.barHeights removeAllObjects]; // 计算总宽度 CGFloat totalWidth = self.barCount * self.barWidth + (self.barCount - 1) * self.barSpacing; CGFloat startX = (self.bounds.size.width - totalWidth) / 2; CGFloat maxHeight = self.bounds.size.height; CGFloat minHeight = maxHeight * 0.2; for (NSInteger i = 0; i < self.barCount; i++) { CAShapeLayer *barLayer = [CAShapeLayer layer]; barLayer.fillColor = self.waveColor.CGColor; barLayer.cornerRadius = self.barWidth / 2; CGFloat x = startX + i * (self.barWidth + self.barSpacing); CGFloat height = minHeight; CGFloat y = (maxHeight - height) / 2; barLayer.frame = CGRectMake(x, y, self.barWidth, height); barLayer.backgroundColor = self.waveColor.CGColor; [self.layer addSublayer:barLayer]; [self.barLayers addObject:barLayer]; [self.barHeights addObject:@(height)]; } } #pragma mark - Public Methods - (void)updateWithRMS:(float)rms { self.targetRMS = MIN(MAX(rms, 0), 1); } - (void)startIdleAnimation { if (self.isAnimating) return; self.isAnimating = YES; self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateAnimation)]; [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; } - (void)stopAnimation { self.isAnimating = NO; [self.displayLink invalidate]; self.displayLink = nil; } - (void)reset { self.currentRMS = 0; self.targetRMS = 0; [self updateBarsWithRMS:0]; } #pragma mark - Animation - (void)updateAnimation { // 平滑过渡到目标 RMS CGFloat smoothing = 0.3; self.currentRMS = self.currentRMS + (self.targetRMS - self.currentRMS) * smoothing; [self updateBarsWithRMS:self.currentRMS]; } - (void)updateBarsWithRMS:(float)rms { CGFloat maxHeight = self.bounds.size.height; CGFloat minHeight = maxHeight * 0.2; CGFloat range = maxHeight - minHeight; // 为每个条添加略微不同的高度和相位 NSTimeInterval time = CACurrentMediaTime(); for (NSInteger i = 0; i < self.barLayers.count; i++) { CAShapeLayer *layer = self.barLayers[i]; // 添加基于时间的波动效果 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 heightFactor = rms * wave; CGFloat height = minHeight + range * heightFactor; height = MAX(minHeight, MIN(maxHeight, height)); // 更新位置 CGFloat y = (maxHeight - height) / 2; [CATransaction begin]; [CATransaction setDisableActions:YES]; layer.frame = CGRectMake(layer.frame.origin.x, y, self.barWidth, height); [CATransaction commit]; } } - (void)dealloc { [self stopAnimation]; } @end