我试图得到自我大小UICollectionViewCells工作与自动布局,但我似乎不能让单元格大小自己的内容。我很难理解如何从单元格的contentView内的内容更新单元格的大小。

下面是我尝试过的设置:

自定义UICollectionViewCell,在它的contentView中有一个UITextView。 UITextView的滚动被禁用。 contentView的水平约束是:"H:|[_textView(320)]",也就是说UITextView被固定在单元格的左边,显式宽度为320。 contentView的垂直约束是:"V:|-0-[_textView]",也就是说UITextView固定在单元格的顶部。 UITextView有一个高度约束设置为常数,UITextView报告将适合文本。

下面是单元格背景设置为红色,UITextView背景设置为蓝色时的效果:

我把我一直在GitHub上玩的项目放在这里。


当前回答

内容查看主播奥秘:

在一个奇怪的例子中

    contentView.translatesAutoresizingMaskIntoConstraints = false

不会起作用。在contentView中添加了四个显式的锚,它工作了。

class AnnoyingCell: UICollectionViewCell {
    
    @IBOutlet var word: UILabel!
    
    override init(frame: CGRect) {
        super.init(frame: frame); common() }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder); common() }
    
    private func common() {
        contentView.translatesAutoresizingMaskIntoConstraints = false
        
        NSLayoutConstraint.activate([
            contentView.leftAnchor.constraint(equalTo: leftAnchor),
            contentView.rightAnchor.constraint(equalTo: rightAnchor),
            contentView.topAnchor.constraint(equalTo: topAnchor),
            contentView.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])
    }
}

像往常一样

    estimatedItemSize = UICollectionViewFlowLayout.automaticSize

在你的布局:UICollectionViewFlowLayout

谁知道呢?也许能帮到别人。

信贷

https://www.vadimbulavin.com/collection-view-cells-self-sizing/

无意中发现了这个技巧——在其他1000篇文章中从未见过。

其他回答

编辑11/19/19:对于iOS 13,只使用UICollectionViewCompositionalLayout估计高度。不要浪费时间处理这个坏的API。

在与此斗争了一段时间后,我注意到,如果你不禁用滚动,调整大小对UITextViews不起作用:

let textView = UITextView()
textView.scrollEnabled = false

在iOS10中有一个名为UICollectionViewFlowLayout的新常量。automaticSize(以前的UICollectionViewFlowLayoutAutomaticSize),所以改为:

self.flowLayout.estimatedItemSize = CGSize(width: 100, height: 100)

你可以用这个:

self.flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

它具有更好的性能,特别是当集合视图中的单元格具有恒定宽度时。

访问流布局:

override func viewDidLoad() {
   super.viewDidLoad()

   if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
      flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
   }
}

Swift 5更新:

override func viewDidLoad() {
   super.viewDidLoad()

   if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout {
      flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
    }
}

解决方案包括3个简单步骤:

启用动态单元格大小

flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

设置来自collectionView(:cellForItemAt:)的containerview . widthachor .constraint来限制contentView的宽度为collectionView的宽度。

class ViewController: UIViewController, UICollectionViewDataSource {
    ...

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! MultiLineCell
        cell.textView.text = dummyTextMessages[indexPath.row]
        cell.maxWidth = collectionView.frame.width
        return cell
    }

    ...
}

class MultiLineCell: UICollectionViewCell{
    ....

    var maxWidth: CGFloat? {
        didSet {
            guard let maxWidth = maxWidth else {
                return
            }
            containerViewWidthAnchor.constant = maxWidth
            containerViewWidthAnchor.isActive = true
        }
    }

    ....
}

因为你想要启用UITextView的自调整大小,它有一个额外的步骤来;

3.计算和设置高度tanchor。常量的UITextView。

当contentView的宽度被设置时,我们会在didSet的maxWidth中调整UITextView的高度。

UICollectionViewCell Inside job:

var maxWidth: CGFloat? {
    didSet {
        guard let maxWidth = maxWidth else {
            return
        }
        containerViewWidthAnchor.constant = maxWidth
        containerViewWidthAnchor.isActive = true
        
        let sizeToFitIn = CGSize(width: maxWidth, height: CGFloat(MAXFLOAT))
        let newSize = self.textView.sizeThatFits(sizeToFitIn)
        self.textViewHeightContraint.constant = newSize.height
    }
}

这些步骤会让你得到想要的结果。

完整的可运行的要旨

参考:Vadim Bulavin博客文章-集合视图单元格自我调整:一步一步的教程

截图:

上面的示例方法不能编译。以下是更正后的版本(但未经测试是否有效)。

override func preferredLayoutAttributesFittingAttributes(layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes 
{
    let attr: UICollectionViewLayoutAttributes = layoutAttributes.copy() as! UICollectionViewLayoutAttributes

    var newFrame = attr.frame
    self.frame = newFrame

    self.setNeedsLayout()
    self.layoutIfNeeded()

    let desiredHeight: CGFloat = self.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height
    newFrame.size.height = desiredHeight
    attr.frame = newFrame
    return attr
}

如果你实现uicollectionviewdelegatflowlayout方法:

- (CGSize)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath*)indexPath

当你调用collectionview performBatchUpdates:completion:时,size height将使用sizeForItemAtIndexPath代替 preferredLayoutAttributesFittingAttributes。

performBatchUpdates:completion的呈现过程将通过preferredLayoutAttributesFittingAttributes方法,但它会忽略您的更改。