From 477a64ecb490891c0342fa4679c70d735bef391e Mon Sep 17 00:00:00 2001 From: link2026 Date: Wed, 10 Jun 2026 08:04:15 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E8=AF=AD=E9=9F=B3=E6=97=A5=E8=AE=B0):=20di?= =?UTF-8?q?ctation=20=E6=9C=8D=E5=8A=A1=E6=94=B9=20@State=20=E9=98=B2?= =?UTF-8?q?=E8=A7=86=E5=9B=BE=E9=87=8D=E5=BB=BA=E4=B8=A2=E5=AE=9E=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct View 重建时普通 let 属性会换成全新 SpeechDictationService, stop() 落在没在录音的新实例上返回空串 → 误报「没听清,再试一次」, 且真正在录音的老实例关不掉(麦克风悬挂)。改 @State 保证实例唯一; 停止时若服务仍返回空,用 @State 实时字幕兜底(用户看到什么就用什么)。 Co-Authored-By: Claude Fable 5 --- 康康/Features/Diary/DiaryQuickSheet.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/康康/Features/Diary/DiaryQuickSheet.swift b/康康/Features/Diary/DiaryQuickSheet.swift index 462807a..1e05ded 100644 --- a/康康/Features/Diary/DiaryQuickSheet.swift +++ b/康康/Features/Diary/DiaryQuickSheet.swift @@ -53,7 +53,10 @@ struct DiaryQuickSheet: View { @State private var voiceDeniedAlert = false @State private var voiceFlowTask: Task? @State private var recordingWatchdog: Task? - private let dictation = SpeechDictationService() + /// 必须 @State:struct View 重建(键盘收起/detent 变化都会触发)时普通 let 会换成 + /// 全新实例,导致 stop() 落在没在录音的新服务上返回空串(「没听清」假错误), + /// 且真正在录音的老实例关不掉、麦克风悬挂。@State 保证视图身份期内实例唯一。 + @State private var dictation = SpeechDictationService() private var hasContent: Bool { !content.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty @@ -590,8 +593,13 @@ struct DiaryQuickSheet: View { guard voicePhase == .recording else { return } recordingWatchdog?.cancel() voiceFlowTask = Task { @MainActor in - let transcript = (await dictation.stop()) + // 防御兜底:服务返回空(极端情况下实例丢失/最终结果丢失)时, + // 用 @State 里的实时字幕——那就是用户亲眼看到的已识别文字。 + var transcript = (await dictation.stop()) .trimmingCharacters(in: .whitespacesAndNewlines) + if transcript.isEmpty { + transcript = liveTranscript.trimmingCharacters(in: .whitespacesAndNewlines) + } liveTranscript = transcript guard !transcript.isEmpty else { withAnimation(.snappy(duration: 0.2)) { voicePhase = .idle }