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

如何解决UITableViewCell内UITextView截图失败的问题?

UITableViewCell内UITextView截图失败的解决方案

问题场景

尝试对UITableViewCell内的UITextView进行截图,使用的单元格代码:

class MyTableViewCell: UITableViewCell {
    @IBOutlet weak var myTextView: UITextView!

    func bind(){
        let renderer = UIGraphicsImageRenderer(size: self.myTextView.bounds.size)
        let myImage = renderer.image { (context) in
            var bounds = self.myTextView.bounds
            bounds.origin = CGPoint.zero
            self.myTextView.drawHierarchy(in: bounds, afterScreenUpdates: true)
        }
        // 后续处理myImage
    }
}

bind()方法在tableView:cellForRowAt:中调用:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
    let myCell = tableView.dequeueReusableCell(withIdentifier: "MyTableViewCell", for: indexPath ) as! MyTableViewCell
    myCell.bind()
    return myCell
}

遇到的错误

  • 设置afterScreenUpdates: true时,抛出错误且myImage为nil:

[Snapshotting] View (UITextView) drawing with afterScreenUpdates:YES inside CoreAnimation commit is not supported.

  • 设置afterScreenUpdates: false时,同样抛出错误且myImage为nil:

[Snapshotting] Rendering a view (UITextView) that has not been rendered at least once requires afterScreenUpdates:YES.

  • 尝试使用layer.render方式截图,myImage仍为nil:
let rect = self.myTextView.bounds
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)

let context: CGContext = UIGraphicsGetCurrentContext()!
self.myTextView.layer.render(in: context)
let myImage : UIImage = UIGraphicsGetImageFromCurrentImageContext()!

UIGraphicsEndImageContext()

解决方案

1. 延迟到视图完成渲染后再截图

在cellForRowAt:阶段,单元格还未完成CoreAnimation的渲染提交,此时直接调用截图会触发冲突。可以将截图逻辑放到主队列的异步任务中,等待当前渲染周期完成后执行:

func bind(){
    DispatchQueue.main.async {
        let renderer = UIGraphicsImageRenderer(size: self.myTextView.bounds.size)
        let myImage = renderer.image { (context) in
            self.myTextView.drawHierarchy(in: self.myTextView.bounds, afterScreenUpdates: false)
        }
        // 在这里处理生成的myImage
    }
}

2. 切换到willDisplayCell代理方法中执行截图

UITableView的willDisplay:forRowAt:方法会在单元格即将显示到屏幕上时调用,此时视图已经完成布局和初始渲染,适合执行截图操作:

// 替换cellForRowAt中的bind调用,改为在willDisplay中执行
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
    let myCell = tableView.dequeueReusableCell(withIdentifier: "MyTableViewCell", for: indexPath ) as! MyTableViewCell
    return myCell
}

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    guard let myCell = cell as? MyTableViewCell else { return }
    myCell.bind()
}

3. 强制视图完成布局后再截图

如果需要在bind方法内立即尝试截图,可以先强制TextView完成布局,再结合异步任务执行:

func bind(){
    // 强制TextView完成当前布局
    myTextView.layoutIfNeeded()
    // 等待布局完成后执行截图
    DispatchQueue.main.async {
        let renderer = UIGraphicsImageRenderer(size: self.myTextView.bounds.size)
        let myImage = renderer.image { (context) in
            self.myTextView.drawHierarchy(in: self.myTextView.bounds, afterScreenUpdates: false)
        }
        // 处理生成的图片
    }
}

错误原因说明

  • cellForRowAt:方法属于CoreAnimation的提交阶段,此时视图还未完成渲染,调用drawHierarchy(with:afterScreenUpdates:)会触发系统的快照冲突。
  • layer.render(in:)方式失效,是因为UITextView的文字内容属于UIKit层面的绘制逻辑,图层渲染时可能还未完成文字的绘制,导致截图为空。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 17:53:16