iOS-阅读器系列

Swift 给一段话分句,或将一句话关键词分组

2018-08-15  本文已影响74人  四月_Hsu

场景

目前正在做阅读器听书功能,集成了百度语音后,每次要给语音SDK传入一句话,然后得到语音。但是有长度限制。为了语音流畅,需要将一章文字按句分组,之后管理一个队列数组,用于语音功能。

如何给一段话分句

实践一:使用 Stingcomponents(separatedBy: ) 方法

其实最开始是想通过正则表达式分组,最终未能实现。之后使用该方法。其实分组不仅仅可以传入单个字符,也能传入一个字符组:

        components(separatedBy: <#T##StringProtocol#>)
        components(separatedBy: <#T##CharacterSet#>)

这个也是我最初采用的方法。但是之后发现存在比较严重的问题,并且并未解决。

存在问题:首先,分组后的语句是不包含作为分隔符的标点符号的,这样对于阅读器切页时每页的 Range 就产生了影响,没法精确判断读到该页末尾时翻页、翻章。其次,阅读文字时需要字体高亮,但是通过该方法分组后的语句,对于作为分隔符的标点符号,如果紧邻其他符号,其他符号为氛围下一句,最典型的就是 。” ,分组后,下引号会分到下一句,明显很难看。

实现代码如下:

       let str = "我说:“这是一句话。”而且按照现在的情况来看,这个奥术还是特别加强了稳固性和构成速度!顷刻之间就要爆发了。“混蛋……居然是这样的陷阱…”女法师脸上浮现出了悲愤,无奈,绝望。"
        let matchs = str.components(separatedBy: CharacterSet.init(charactersIn: " 。!?;\n "))
        matchs.forEach { (s) in
            print("🌞 --- \(s)")
        }

控制台输出:

🌞 --- 我说:“这是一句话
🌞 --- ”而且按照现在的情况来看,这个奥术还是特别加强了稳固性和构成速度
🌞 --- 顷刻之间就要爆发了
🌞 --- “混蛋……居然是这样的陷阱…”女法师脸上浮现出了悲愤,无奈,绝望
🌞 --- 

存在问题显而易见,尤其是需要这句话高亮时,问题更明显。由此,查资料时找到了第二种方法。

实践二:采用原生 API 接口 CFStringTokenizer

CFStringTokenizer功能很强大,之前也从未使用过,这里也只是通过查询一些博客,采用了自己需要的一点功能。

        let str = "我说:“这是一句话。”而且按照现在的情况来看,这个奥术还是特别加强了稳固性和构成速度!顷刻之间就要爆发了。“混蛋……居然是这样的陷阱…”女法师脸上浮现出了悲愤,无奈,绝望。"
        let ref = CFStringTokenizerCreate(nil, str as CFString, CFRangeMake(0, str.lenght), kCFStringTokenizerUnitSentence, nil)
        CFStringTokenizerAdvanceToNextToken(ref)
        var range: CFRange
        range = CFStringTokenizerGetCurrentTokenRange(ref)
        // 循环
        var sentence = ""
        var sentences = [String]()
        while range.length > 0 {
            sentence = str.substring(NSMakeRange(range.location, range.length))
            sentences.append(sentence)
            CFStringTokenizerAdvanceToNextToken(ref)
            range = CFStringTokenizerGetCurrentTokenRange(ref)
            print("🦁    \(sentence)")
        }

控制台输出:

🦁    我说:“这是一句话。”
🦁    而且按照现在的情况来看,这个奥术还是特别加强了稳固性和构成速度!
🦁    顷刻之间就要爆发了。“
🦁    混蛋……居然是这样的陷阱…”女法师脸上浮现出了悲愤,无奈,绝望。

是不是符合预期的结果直接就出来了。。暂时效果如下图:


听书

如果修改属性 kCFStringTokenizerUnitSentencekCFStringTokenizerUnitWord ,就是按照关键词分组了。其实这个属性还有其他各种设置,具体使用时可以根据需要修改。
另外,关键词分组效果类似:

🦁    我
🦁    说
🦁    这
🦁    是
🦁    一
🦁    句
🦁    话
🦁    而且
🦁    按照
🦁    现在
🦁    的

如何暂停倒计时

这个也是刚刚用到的一个方法。一旦 timer.invalidate() 后,并不能在开启了。下面是一个简单的计时器实现:

    // MARK: - 计时器
    @IBOutlet weak var timeView: UIView!
    @IBOutlet weak var timeLabel: UILabel!
    private var countdownTimer: Timer?
    private var durtion: Int = 0
    // 开始
    @IBAction func startTimerAction(_ sender: UIButton) {
        if countdownTimer == nil {
            countdownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(countdown), userInfo: nil, repeats: true)
        }
        durtion = 0
    }
    // 暂停
    @IBAction func pauseTimerAction(_ sender: UIButton) {
        countdownTimer?.fireDate = Date.distantFuture
    }
    // 继续
    @IBAction func resumeTimerAction(_ sender: UIButton) {
        countdownTimer?.fireDate = Date.distantPast
    }
    // 停止
    @IBAction func stopTimerAction(_ sender: UIButton) {
        countdownTimer?.invalidate()
        countdownTimer = nil
    }
    // 计时
    @objc private func countdown() {
        durtion += 1
        let delaySeconds = 30 * 60 - durtion
        let minutes = delaySeconds / 60
        let seconds = delaySeconds % 60
        let timeStr = String(format: "%02d:%02d", arguments: [minutes, seconds])
        timeLabel.text = timeStr
    }
上一篇下一篇

猜你喜欢

热点阅读