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

UICollectionView实现带自适应高度UITextView的可扩展单元格

问题核心原因

你当前的垂直方向约束链断裂,同时缺少必要的布局刷新逻辑:内嵌容器UIView没有和内部UITextView绑定底部约束,Auto Layout无法通过textView的内容高度反推容器、单元格的最终高度,UICollectionView拿不到正确的内容尺寸,自然无法同步更新contentSize触发正常滚动。另外你单元格的约束代码写错了约束对象,还漏了UITextView的关键属性配置。

分步实现方案
  • 第一步:修正UITextView基础配置
    首先关闭UITextView的滚动,这是让textView通过自身内容输出固有高度的核心前提,开着滚动的话textView内容超出后会在内部滚动,不会向外传递高度变化:
    private var textView: UITextView = {
        let textView = UITextView()
        textView.translatesAutoresizingMaskIntoConstraints = false
        textView.isUserInteractionEnabled = true
        textView.isEditable = true
        textView.isScrollEnabled = false // 新增核心配置
        return textView
    }()
    
  • 第二步:补全内嵌容器视图的约束,删除冗余固定宽度
    你之前给textView写的312固定宽度是多余的,textView左右已经贴合容器父视图,宽度会由父视图自动约束。重点补上textView和容器视图的底部约束,打通垂直方向的约束链路:
    private func setConstraints() {
        textView.topAnchor.constraint(equalTo: topAnchor).isActive = true
        textView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
        textView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true
        // 新增底部约束,让容器高度和textView内容高度绑定
        textView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
    }
    
  • 第三步:修正单元格的约束逻辑
    你之前单元格的约束代码直接约束textView是错误的,textView属于内嵌容器视图,单元格需要约束的是容器视图,且所有子视图必须添加在cell的contentView上,不要直接加在cell本体上,否则会出现约束计算偏差:
    // 先把容器视图添加到contentView
    contentView.addSubview(yourContainerView)
    yourContainerView.translatesAutoresizingMaskIntoConstraints = false
    
    func setConstraints() {
        yourContainerView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
        yourContainerView.leftAnchor.constraint(equalTo: contentView.leftAnchor).isActive = true
        yourContainerView.rightAnchor.constraint(equalTo: contentView.rightAnchor).isActive = true
        yourContainerView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor).isActive = true
    }
    
  • 第四步:配置UICollectionView开启自动尺寸
    给collectionView的layout设置估算尺寸,让系统自动计算单元格高度,不要写死itemSize:
    let flowLayout = UICollectionViewFlowLayout()
    // 给一个接近实际值的估算尺寸即可,能提升布局计算效率
    flowLayout.estimatedItemSize = CGSize(width: 312, height: 50)
    yourCollectionView.collectionViewLayout = flowLayout
    
  • 第五步:监听文本输入,触发布局刷新
    UITextView输入内容时不会自动通知collectionView更新布局,需要在textView的代理方法中触发布局更新,同时处理滚动位置避免输入时光标跳走:
    func textViewDidChange(_ textView: UITextView) {
        let currentOffset = yourCollectionView.contentOffset
        UIView.performWithoutAnimation {
            yourCollectionView.performBatchUpdates(nil) { _ in
                // 布局更新后还原滚动位置,避免输入时页面跳动
                yourCollectionView.setContentOffset(currentOffset, animated: false)
            }
        }
    }
    
避坑注意
  • 不要给textView、容器视图、cell的contentView添加任何固定高度约束,否则会覆盖自适应计算的高度
  • 不要手动计算textView的高度赋值给约束,Auto Layout会根据textView的字体、边距、内容自动计算准确高度,手动计算容易因为边距、系统版本差异出问题
  • 如果输入超长文本时出现卡顿,可以给textView单独添加高度约束,在textViewDidChange里计算内容高度更新约束值,比全量刷新布局性能更好,常规输入场景下用上面的批量更新方案足够。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 23:42:19