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

iOS开发:实现TextView达第7行时自动滚动至顶部并追加内容

实现iOS TextView实时滚动并自动截断旧内容(保持指定行数显示)

嗨,我明白你想要实现的是语音识别实时文本显示的滚动效果——当内容到第7行末尾时,旧内容自动被顶掉,最新的内容始终留在可视区域,对吧?我之前也做过类似的需求,给你几个可行的解决方案:

方案一:自定义TextView内容处理,实时截断超出行数的旧内容

这个方案直接基于你现有的TextView,核心思路是每次追加文本后,检查当前行数是否超过7,超过就自动截断最前面的内容,只保留最后7行,同时确保滚动到最新内容位置。

1. 先实现一个计算TextView行数的工具方法

func getTextViewLineCount(_ textView: UITextView) -> Int {
    guard let layoutManager = textView.layoutManager, let textContainer = textView.textContainer else {
        return 0
    }
    let glyphRange = layoutManager.glyphRange(for: textContainer)
    let lineRects = layoutManager.lineFragmentRects(forGlyphRange: glyphRange, withoutAdditionalLayout: true)
    return lineRects.count
}

2. 在追加语音文本的逻辑中加入截断处理

假设你有一个接收语音识别结果的方法,在里面做如下处理:

func appendRecognizedText(_ text: String) {
    // 追加新文本
    textView.text.append(text)
    
    // 检查当前行数是否超过7
    let currentLineCount = getTextViewLineCount(textView)
    if currentLineCount > 7 {
        guard let layoutManager = textView.layoutManager, let textContainer = textView.textContainer else {
            return
        }
        
        // 获取最后7行的起始字符位置
        let glyphRange = layoutManager.glyphRange(for: textContainer)
        let lineRects = layoutManager.lineFragmentRects(forGlyphRange: glyphRange, withoutAdditionalLayout: true)
        let startGlyphIndex = layoutManager.glyphIndex(for: lineRects[lineRects.count - 7].origin)
        let stringRange = layoutManager.characterRange(forGlyphRange: NSRange(location: startGlyphIndex, length: glyphRange.length - startGlyphIndex), actualGlyphRange: nil)
        
        // 截断文本,只保留最后7行
        if let newText = textView.text?.substring(with: stringRange) {
            textView.text = newText
        }
    }
    
    // 滚动到内容底部,确保最新文本可见
    let bottomOffset = CGPoint(x: 0, y: textView.contentSize.height - textView.bounds.size.height)
    textView.setContentOffset(bottomOffset, animated: false)
}

方案二:用UITableView实现更灵活的内容控制

如果你觉得TextView的行数计算有点麻烦,用UITableView会更直观——只需要维护一个数据源数组,每次新增文本时,控制数组最多保留7条(或者1条,满足你“只显示最后一行”的需求),然后刷新表格并滚动到最后一行。

1. 准备数据源和基础配置

// 存储语音识别文本的数据源
var recognizedTexts: [String] = []
// 表格单元格复用标识符
let speechCellIdentifier = "SpeechTextCell"

// 在viewDidLoad里注册单元格
override func viewDidLoad() {
    super.viewDidLoad()
    tableView.register(UITableViewCell.self, forCellReuseIdentifier: speechCellIdentifier)
    tableView.rowHeight = UITableView.automaticDimension
    tableView.estimatedRowHeight = 44
}

2. 追加文本并控制数据源

func appendRecognizedText(_ text: String) {
    recognizedTexts.append(text)
    // 超过7条就移除最旧的第一条
    if recognizedTexts.count > 7 {
        recognizedTexts.removeFirst()
    }
    // 如果只想显示最后一行,把上面的判断改成`if recognizedTexts.count > 1`即可
    tableView.reloadData()
    
    // 滚动到最后一行,确保最新内容可见
    if recognizedTexts.count > 0 {
        let lastIndexPath = IndexPath(row: recognizedTexts.count - 1, section: 0)
        tableView.scrollToRow(at: lastIndexPath, at: .bottom, animated: false)
    }
}

3. 实现UITableView的代理方法

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return recognizedTexts.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: speechCellIdentifier, for: indexPath)
    cell.textLabel?.text = recognizedTexts[indexPath.row]
    cell.textLabel?.numberOfLines = 0 // 允许文本换行
    return cell
}

小提示

在实时语音识别的场景中,尽量避免过于频繁的文本更新(比如每识别一个字就更新一次),可以攒一小段文本再处理,这样能减少性能消耗,让滚动更流畅~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:21:43