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

下面是我尝试过的设置:

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

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

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


当前回答

更新更多信息:

If you use flowLayout.estimatedItemSize, suggest use iOS8.3 later version. Before iOS8.3, it will crash [super layoutAttributesForElementsInRect:rect];. The error message is *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil' Second, in iOS8.x version, flowLayout.estimatedItemSize will cause different section inset setting did not work. i.e. function: (UIEdgeInsets)collectionView:layout:insetForSectionAtIndex:.

其他回答

解决方案包括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方法,但它会忽略您的更改。

对于那些尝试一切都没有运气的人来说,这是唯一让它为我工作的东西。 对于单元格内的多行标签,尝试添加以下神奇的行:

label.preferredMaxLayoutWidth = 200

更多信息:点击这里

干杯!

在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
    }
}