You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI项目语音朗读速度调整异常:修改后回到开头需修复

解决SwiftUI中AVSpeechSynthesizer调整朗读速度不跳转至开头的问题

问题根源

你当前的代码每次调整速度都会强制停止朗读,然后重新调用startReading()创建全新的AVSpeechUtterance并从头开始朗读,这就是导致跳转至开头的核心原因。要实现无跳转的速度调整,需要分播放中实时修改和暂停后从当前位置恢复两种场景针对性处理。

解决方案与代码修改

1. 添加必要的实例变量

先补充用于缓存内容、记录进度的变量:

// 缓存完整PDF文本,避免重复解析
private var fullDocumentContent: String = ""
// 记录当前朗读进度,用于暂停后恢复
private var currentSpeechProgress: TimeInterval = 0.0
// 语音合成器实例
private let speechSynthesizer = AVSpeechSynthesizer()
// 当前使用的语音 utterance
private var currentUtterance: AVSpeechUtterance?
// 速度配置(可根据需求调整)
private var speedIndex = 0
private let speechSpeeds: [Double] = [0.5, 1.0, 1.5, 2.0]
// 播放状态标记
private var isPlaying = false

2. 重构startReading方法,支持指定进度朗读

修改方法逻辑,只解析一次PDF内容,并允许从指定进度开始朗读:

func startReading(from progress: TimeInterval = 0.0) {
    // 首次调用时解析PDF并缓存内容
    if fullDocumentContent.isEmpty {
        guard let pdfDocument = pdfDocument else { return }
        var contentBuilder = ""
        for pageIndex in 0..<pdfDocument.pageCount {
            guard let page = pdfDocument.page(at: pageIndex), let pageText = page.string else { continue }
            contentBuilder.append(pageText)
        }
        fullDocumentContent = contentBuilder
        guard !fullDocumentContent.isEmpty else { return }
    }
    
    // 停止当前朗读(如果正在进行)
    if speechSynthesizer.isSpeaking {
        speechSynthesizer.stopSpeaking(at: .immediate)
    }
    
    // 创建新utterance并设置当前速度
    currentUtterance = AVSpeechUtterance(string: fullDocumentContent)
    let targetRate = AVSpeechUtteranceMaximumSpeechRate * Float(CGFloat(speechSpeeds[speedIndex])) / 2.0
    currentUtterance?.rate = targetRate
    
    // 设置从指定进度开始朗读
    if let utterance = currentUtterance, progress > 0 {
        currentUtterance?.speechTimeRange = TimeRange(
            start: progress,
            duration: utterance.speechDuration - progress
        )
    }
    
    speechSynthesizer.speak(currentUtterance!)
    isPlaying = true
}

3. 实现暂停/恢复功能,记录朗读进度

处理暂停状态下的进度保存,确保恢复时从当前位置继续:

func togglePlayPause() {
    if speechSynthesizer.isSpeaking {
        // 暂停时记录当前朗读位置
        speechSynthesizer.pauseSpeaking(at: .word)
        if let utterance = speechSynthesizer.currentUtterance {
            currentSpeechProgress = speechSynthesizer.currentTime(for: utterance)
        }
        isPlaying = false
    } else {
        // 恢复时从记录的进度开始,使用当前设置的速度
        startReading(from: currentSpeechProgress)
    }
}

4. 修改increaseSpeed方法,实现无跳转调整

直接修改当前朗读的utterance速率,无需停止重新开始:

func increaseSpeed() {
    speedIndex = (speedIndex + 1) % speechSpeeds.count
    let newRate = AVSpeechUtteranceMaximumSpeechRate * Float(CGFloat(speechSpeeds[speedIndex])) / 2.0
    
    if speechSynthesizer.isSpeaking {
        // 播放中直接修改当前utterance的速率,实时生效
        speechSynthesizer.currentUtterance?.rate = newRate
    } else {
        // 暂停状态下更新缓存的utterance速率,下次恢复时使用
        currentUtterance?.rate = newRate
    }
}

同理,decreaseSpeed方法可以镜像实现:

func decreaseSpeed() {
    speedIndex = (speedIndex - 1 + speechSpeeds.count) % speechSpeeds.count
    let newRate = AVSpeechUtteranceMaximumSpeechRate * Float(CGFloat(speechSpeeds[speedIndex])) / 2.0
    
    if speechSynthesizer.isSpeaking {
        speechSynthesizer.currentUtterance?.rate = newRate
    } else {
        currentUtterance?.rate = newRate
    }
}

关键逻辑说明

  • 实时速度调整:利用AVSpeechSynthesizer.currentUtterance直接修改rate属性,系统会立即应用新速度,无需中断朗读。
  • 进度记录与恢复:暂停时通过speechSynthesizer.currentTime(for:)获取当前朗读位置,恢复时通过speechTimeRange设置utterance的起始朗读点。
  • 内容缓存:仅解析一次PDF内容,避免重复IO操作,提升响应速度。

内容的提问来源于stack exchange,提问作者Xan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 00:16:09