代码修改UICollectionView属性崩溃,如何在启动时访问单元格?
解决UICollectionView布局代理里访问cell导致的崩溃问题
兄弟,你这崩溃的坑我以前也踩过!核心问题太典型了——在sizeForItemAt这个方法里调用cellForItem(at:)根本拿不到已经初始化好的cell啊!
为啥会崩溃?
sizeForItemAt是UICollectionView布局代理用来提前计算单元格尺寸的方法,这时候对应的单元格还没被创建出来呢!你强行用as!解包一个nil的cell,不崩溃才怪。而且这个方法的职责就只是返回尺寸,本来就不该用来操作cell的属性。
正确调整TextView Frame的姿势
想要适配不同屏幕调整textView的frame,推荐这几个靠谱的方式:
1. 把布局逻辑放到自定义Cell内部(最推荐)
让Cell自己管理子视图的布局,这才符合MVC的职责分离。重写Cell的layoutSubviews方法,每次Cell尺寸变化时,自动调整textView的frame:
class WordsMainCollectionViewCell: UICollectionViewCell { let userWord = UITextView() override init(frame: CGRect) { super.init(frame: frame) setupTextView() } required init?(coder: NSCoder) { super.init(coder: coder) setupTextView() } private func setupTextView() { addSubview(userWord) // 先做基础配置:关闭滚动、设置字体等 userWord.isScrollEnabled = false userWord.font = UIFont.systemFont(ofSize: 16) userWord.textContainerInset = .zero } override func layoutSubviews() { super.layoutSubviews() // 根据Cell的当前边界,给textView设置边距适配 let textViewInsets = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) userWord.frame = bounds.inset(by: textViewInsets) // 如果需要根据文本内容自动调整textView高度,也可以在这里计算 let fittingSize = userWord.sizeThatFits(CGSize(width: userWord.frame.width, height: CGFloat.greatestFiniteMagnitude)) userWord.frame.size.height = fittingSize.height } }
2. 在cellForItemAt里配置Cell时调整
当Cell真正被创建/复用的时候,在这里根据当前的Cell尺寸设置textView的frame:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "WordsMainCell", for: indexPath) as! WordsMainCollectionViewCell // 配置文本内容 cell.userWord.text = yourDataArray[indexPath.item] // 调整textView frame let textViewInsets = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16) cell.userWord.frame = cell.bounds.inset(by: textViewInsets) // 测试代码可以放这里,不会崩溃 cell.userWord.textColor = .red return cell }
3. 动态计算Cell高度(如果需要)
如果你的Cell高度要根据textView内容自适应,不要拿Cell来计算,直接用模型数据提前算好尺寸:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let targetText = yourDataArray[indexPath.item] let textWidth = collectionView.frame.width - 32 // 减去左右16pt的边距 let textAttributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16)] // 计算文本需要的高度 let textSize = NSString(string: targetText).boundingRect( with: CGSize(width: textWidth, height: CGFloat.greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: textAttributes, context: nil ).size // 加上上下边距的总高度 let cellHeight = textSize.height + 16 return CGSize(width: collectionView.frame.width, height: cellHeight) }
最后再划个重点
sizeForItemAt只负责返回尺寸,别在这儿操作Cell的任何属性!- Cell的子视图布局,优先让Cell自己处理,代码更易维护。
- 动态高度计算依赖模型数据,不要依赖未创建的Cell实例。
内容的提问来源于stack exchange,提问作者user9119326
相关产品推荐
相关产品推荐

