UICollectionView因NSAttributedString图片导致滚动卡顿问题求助
嘿,这个滚动卡顿的问题我之前也碰到过,大概率是带图片的富文本渲染和Cell复用没做好优化导致的。给你几个实用的解决思路,亲测有效:
解决方案
1. 预加载并缓存默认的带图富文本
每次Cell复用都重新创建带图片的NSAttributedString是很大的性能消耗——毕竟图片附件的初始化、排版计算都是耗时操作。建议提前在全局或ViewModel里预创建好这个默认富文本并缓存:
// 全局缓存的默认富文本,只初始化一次 let emptyRecordAttributedString: NSAttributedString = { let attachment = NSTextAttachment() // 提前加载并缩放好适配TextView的图片,避免临时缩放开销 let originalImage = UIImage(named: "empty-record-icon")! let targetSize = CGSize(width: 40, height: 40) let scaledImage = UIGraphicsImageRenderer(size: targetSize).image { _ in originalImage.draw(in: CGRect(origin: .zero, size: targetSize)) } attachment.image = scaledImage attachment.bounds = CGRect(origin: .zero, size: targetSize) let mutableAttrStr = NSMutableAttributedString(attachment: attachment) mutableAttrStr.append(NSAttributedString(string: "\n今天还没有记录哦~")) // 统一设置字体样式,避免TextView重复计算排版 let fullRange = NSRange(location: 0, length: mutableAttrStr.length) mutableAttrStr.addAttributes([ .font: UIFont.systemFont(ofSize: 16), .foregroundColor: UIColor.secondaryLabel ], range: fullRange) return mutableAttrStr }()
之后Cell需要显示默认内容时,直接复用这个预创建的对象就行,不用每次都重复造轮子。
2. 优化UICollectionViewCell的复用逻辑
Cell复用是性能优化的核心,一定要避免在cellForItemAt里做耗时操作:
- 在
prepareForReuse()里轻量重置状态,避免旧内容干扰:
override func prepareForReuse() { super.prepareForReuse() // 清空TextView内容,避免滚动时出现内容闪烁 self.textView.attributedText = nil self.textView.text = nil }
- 提前加载Core Data数据到内存数组,不要在
cellForItemAt里同步查询数据库:
// 在ViewModel或ViewController里提前异步加载所有记录 var dailyRecords: [DailyRecord] = [] func loadAllRecords() { let fetchRequest: NSFetchRequest<DailyRecord> = DailyRecord.fetchRequest() fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)] // 后台线程执行Core Data查询,不阻塞主线程 persistentContainer.viewContext.perform { do { let records = try fetchRequest.execute() DispatchQueue.main.async { self.dailyRecords = records self.collectionView.reloadData() } } catch { print("加载记录失败:\(error.localizedDescription)") } } } // 在cellForItemAt里直接从内存数组取数据 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "DailyCell", for: indexPath) as! DailyCell let record = dailyRecords[indexPath.item] if let content = record.content, !content.isEmpty { cell.textView.text = content } else { // 直接用预缓存的富文本 cell.textView.attributedText = emptyRecordAttributedString } return cell }
3. 降低UITextView的渲染负担
UITextView默认的一些配置会额外增加渲染开销,针对这种只读的默认状态,可以做这些优化:
// 在Cell初始化TextView时配置 func setupTextView() { textView.isEditable = false textView.isScrollEnabled = false textView.backgroundColor = .clear // 关闭不必要的排版内边距,减少渲染区域 textView.textContainerInset = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8) textView.textContainer.lineFragmentPadding = 0 // 开启光栅化,把TextView内容缓存为位图,减少滚动时的重复渲染 textView.layer.shouldRasterize = true textView.layer.rasterizationScale = UIScreen.main.scale }
开启光栅化后,TextView的内容会被缓存成一张图片,滚动时直接复用这张图片,不用重新计算富文本排版。
4. 检查图片资源的优化
如果你的默认图片尺寸过大,即使缩放后也会占用更多内存和渲染时间。建议:
- 使用适配TextView显示尺寸的图片(比如40x40的图标就不要用200x200的原图)
- 把图片转成更高效的格式或者压缩到合适的大小,减少内存占用
内容的提问来源于stack exchange,提问作者user6839561
相关产品推荐
相关产品推荐

