diff --git a/keyBoard/Class/AiTalk/V/KBAiWaveformView.m b/keyBoard/Class/AiTalk/V/KBAiWaveformView.m index 93a7948..17e38f1 100644 --- a/keyBoard/Class/AiTalk/V/KBAiWaveformView.m +++ b/keyBoard/Class/AiTalk/V/KBAiWaveformView.m @@ -14,6 +14,8 @@ @property(nonatomic, assign) float currentRMS; @property(nonatomic, assign) float targetRMS; @property(nonatomic, assign) BOOL isAnimating; +@property(nonatomic, assign) NSInteger debugFrameCount; +@property(nonatomic, assign) CGSize lastLayoutSize; @end @implementation KBAiWaveformView @@ -49,6 +51,11 @@ - (void)layoutSubviews { [super layoutSubviews]; + if (CGSizeEqualToSize(self.lastLayoutSize, self.bounds.size) && + self.barLayers.count == self.barCount) { + return; + } + self.lastLayoutSize = self.bounds.size; [self setupBars]; } @@ -73,7 +80,7 @@ barLayer.cornerRadius = self.barWidth / 2; CGFloat x = startX + i * (self.barWidth + self.barSpacing); - CGFloat height = minHeight; + CGFloat height = maxHeight; // 统一用满高,后续用 transform.scale.y 控制高度 if (self.barHeightPattern.count > i) { CGFloat base = [self.barHeightPattern[i] floatValue]; base = MIN(MAX(base, 0.15), 0.9); @@ -81,7 +88,11 @@ } CGFloat y = (maxHeight - height) / 2; - barLayer.frame = CGRectMake(x, y, self.barWidth, height); + barLayer.frame = CGRectMake(x, 0, self.barWidth, maxHeight); + barLayer.anchorPoint = CGPointMake(0.5, 0.5); + barLayer.position = CGPointMake(x + self.barWidth / 2, maxHeight / 2); + CGFloat scale = height / maxHeight; + barLayer.transform = CATransform3DMakeScale(1, scale, 1); barLayer.backgroundColor = self.waveColor.CGColor; [self.layer addSublayer:barLayer]; @@ -94,24 +105,42 @@ - (void)updateWithRMS:(float)rms { self.targetRMS = MIN(MAX(rms, 0), 1); + NSLog(@"[KBAiWaveformView] updateWithRMS: %.3f, targetRMS=%.3f, barCount=%ld, size=%@", + rms, self.targetRMS, (long)self.barLayers.count, NSStringFromCGRect(self.bounds)); + if (!self.displayLink) { + self.currentRMS = self.targetRMS; + [self updateBarsWithRMS:self.currentRMS]; + } } - (void)startIdleAnimation { - if (self.isAnimating) + NSLog(@"[KBAiWaveformView] startIdleAnimation (animating=%d, bars=%ld, size=%@)", + self.isAnimating, (long)self.barLayers.count, NSStringFromCGRect(self.bounds)); + if (self.isAnimating && self.displayLink) { return; + } self.isAnimating = YES; - self.displayLink = - [CADisplayLink displayLinkWithTarget:self - selector:@selector(updateAnimation)]; + self.debugFrameCount = 0; + [self.displayLink invalidate]; + self.displayLink = [CADisplayLink displayLinkWithTarget:self + selector:@selector(updateAnimation)]; + if (@available(iOS 10.0, *)) { + self.displayLink.preferredFramesPerSecond = 60; + } [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; + NSLog(@"[KBAiWaveformView] displayLink started"); } - (void)stopAnimation { + NSLog(@"[KBAiWaveformView] stopAnimation (bars=%ld)", (long)self.barLayers.count); self.isAnimating = NO; [self.displayLink invalidate]; self.displayLink = nil; + for (CAShapeLayer *layer in self.barLayers) { + [layer removeAnimationForKey:@"kb_idle_scale"]; + } } - (void)reset { @@ -123,8 +152,16 @@ #pragma mark - Animation - (void)updateAnimation { + if (!self.isAnimating) { + return; + } + self.debugFrameCount += 1; + if (self.debugFrameCount % 30 == 0) { + NSLog(@"[KBAiWaveformView] tick (target=%.3f, current=%.3f, bars=%ld)", + self.targetRMS, self.currentRMS, (long)self.barLayers.count); + } // 平滑过渡到目标 RMS - CGFloat smoothing = 0.3; + CGFloat smoothing = 0.65; self.currentRMS = self.currentRMS + (self.targetRMS - self.currentRMS) * smoothing; @@ -144,25 +181,26 @@ // 添加基于时间的波动效果 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 * 8 + phase) * 0.3 + 0.7; // 0.4 - 1.0 CGFloat baseFactor = 0.2; if (self.barHeightPattern.count > i) { baseFactor = [self.barHeightPattern[i] floatValue]; baseFactor = MIN(MAX(baseFactor, 0.15), 0.9); } + // 让基准高度也随时间轻微摆动(长按时更明显) + CGFloat idleWave = sin(time * 10 + phase) * 0.25 + 0.85; // 0.6 - 1.1 + CGFloat baseWave = MIN(MAX(baseFactor * idleWave, 0.15), 0.95); // 计算高度:基准高度 + 随 RMS 波动 - CGFloat dynamicFactor = rms * (0.35 + 0.15 * wave); // 0.35~0.5 - CGFloat heightFactor = MIN(1.0, baseFactor + dynamicFactor * (1.0 - baseFactor)); + CGFloat dynamicFactor = rms * (0.45 + 0.20 * wave); // 0.45~0.65 + CGFloat heightFactor = MIN(1.0, baseWave + dynamicFactor * (1.0 - baseWave)); CGFloat height = maxHeight * heightFactor; height = MAX(minHeight, MIN(maxHeight, height)); - - // 更新位置 - CGFloat y = (maxHeight - height) / 2; + CGFloat scale = height / maxHeight; [CATransaction begin]; [CATransaction setDisableActions:YES]; - layer.frame = CGRectMake(layer.frame.origin.x, y, self.barWidth, height); + layer.transform = CATransform3DMakeScale(1, scale, 1); [CATransaction commit]; } } diff --git a/keyBoard/Class/AiTalk/V/KBVoiceInputBar.m b/keyBoard/Class/AiTalk/V/KBVoiceInputBar.m index 1812a5f..ea5c587 100644 --- a/keyBoard/Class/AiTalk/V/KBVoiceInputBar.m +++ b/keyBoard/Class/AiTalk/V/KBVoiceInputBar.m @@ -228,6 +228,7 @@ - (void)setInputState:(KBVoiceInputBarState)inputState { _inputState = inputState; + NSLog(@"[KBVoiceInputBar] setInputState: %ld", (long)inputState); self.textInputView.hidden = (inputState != KBVoiceInputBarStateText); self.voiceInputView.hidden = (inputState != KBVoiceInputBarStateVoice); self.recordingView.hidden = (inputState != KBVoiceInputBarStateRecording); @@ -256,6 +257,7 @@ [self.recordButton updateVolumeRMS:rms]; if (self.inputState == KBVoiceInputBarStateRecording) { CGFloat safeRMS = MAX(rms, 0.6f); + NSLog(@"[KBVoiceInputBar] updateVolumeRMS: %.3f (safe=%.3f)", rms, safeRMS); [self.leftWaveformView updateWithRMS:safeRMS]; [self.rightWaveformView updateWithRMS:safeRMS]; } @@ -533,6 +535,20 @@ #pragma mark - Recording Wave - (void)startRecordingWaveAnimationIfNeeded { + NSLog(@"[KBVoiceInputBar] startRecordingWaveAnimationIfNeeded"); + self.leftWaveformView.hidden = NO; + self.rightWaveformView.hidden = NO; + [self.inputContainer setNeedsLayout]; + [self.recordingView setNeedsLayout]; + [self.inputContainer layoutIfNeeded]; + [self.recordingView layoutIfNeeded]; + [self.leftWaveformView setNeedsLayout]; + [self.rightWaveformView setNeedsLayout]; + [self.leftWaveformView layoutIfNeeded]; + [self.rightWaveformView layoutIfNeeded]; + NSLog(@"[KBVoiceInputBar] waveform frames L=%@ R=%@", + NSStringFromCGRect(self.leftWaveformView.frame), + NSStringFromCGRect(self.rightWaveformView.frame)); [self.leftWaveformView startIdleAnimation]; [self.rightWaveformView startIdleAnimation]; [self.leftWaveformView updateWithRMS:0.7f]; @@ -540,10 +556,13 @@ } - (void)stopRecordingWaveAnimation { + NSLog(@"[KBVoiceInputBar] stopRecordingWaveAnimation"); [self.leftWaveformView stopAnimation]; [self.rightWaveformView stopAnimation]; [self.leftWaveformView reset]; [self.rightWaveformView reset]; + self.leftWaveformView.hidden = YES; + self.rightWaveformView.hidden = YES; } @end