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

下面是我尝试过的设置:

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

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

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


当前回答

在viewDidLoad()上添加flowLayout override func viewDidLoad() { super.viewDidLoad () if let flowLayout = infoCollection。collectionViewLayout一样吗?UICollectionViewFlowLayout { estimateditemsize = CGSize(宽度:1,高度:1) } } 此外,设置一个UIView为单元的mainContainer,并在其中添加所有必需的视图。 参考这个很棒的,令人兴奋的教程,以获得进一步的参考: 在iOS 9和10中使用自动布局自动调整单元格的UICollectionView

其他回答

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

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

let textView = UITextView()
textView.scrollEnabled = false

如果你实现uicollectionviewdelegatflowlayout方法:

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

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

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

这个答案在iOS 14中已经过时了,因为添加了复合布局。请考虑更新新的API

Swift 5更新

preferredLayoutAttributesFittingAttributes重命名为preferredLayoutAttributesFitting并使用自动调整大小


Swift 4更新

systemLayoutSizeFittingSize重命名为systemlayoutsizefiting


iOS 9更新

在看到我的GitHub解决方案在iOS 9下崩溃后,我终于有时间全面调查这个问题。我现在已经更新了repo,以包括几个不同配置的自调整单元格的示例。我的结论是,自我调整单元格在理论上很好,但在实践中很混乱。在处理自调整单元格时要注意一点。

博士TL;

看看我的GitHub项目


自我调整单元格只支持流布局,所以要确保这是你正在使用的。

要使自调整大小的单元格工作,需要设置两件事。

# 1。在UICollectionViewFlowLayout上设置estimatedItemSize

一旦您设置了estimatedItemSize属性,流布局将在本质上成为动态的。

self.flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize

# 2。在单元格子类上添加大小调整支持

这有两种口味;自动布局或自定义覆盖preferredLayoutAttributesFittingAttributes。

使用自动布局创建和配置单元格

我不会详细介绍这一点,因为有一篇关于为单元配置约束的精彩SO帖子。只是要小心,Xcode 6打破了一大堆东西与iOS 7,所以,如果你支持iOS 7,你将需要做的事情,如确保autoresizingMask设置在单元格的contentView和contentView的边界设置为单元格的边界时加载单元格(即awakeFromNib)。

你需要注意的是你的单元格需要比表格视图单元格更严格的约束。例如,如果你想要你的宽度是动态的,那么你的单元格需要一个高度约束。同样地,如果你想要高度是动态的,那么你需要一个宽度约束你的单元格。

在自定义单元格中实现preferredLayoutAttributesFittingAttributes

当这个函数被调用时,你的视图已经配置了内容(即cellForItem已经被调用)。假设你已经适当地设置了约束,你可以有一个这样的实现:

//forces the system to do one layout pass
var isHeightCalculated: Bool = false

override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
    //Exhibit A - We need to cache our calculation to prevent a crash.
    if !isHeightCalculated {
        setNeedsLayout()
        layoutIfNeeded()
        let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
        var newFrame = layoutAttributes.frame
        newFrame.size.width = CGFloat(ceilf(Float(size.width)))
        layoutAttributes.frame = newFrame
        isHeightCalculated = true
    }
    return layoutAttributes
}

NOTE On iOS 9 the behaviour changed a bit that could cause crashes on your implementation if you are not careful (See more here). When you implement preferredLayoutAttributesFittingAttributes you need to ensure that you only change the frame of your layout attributes once. If you don't do this the layout will call your implementation indefinitely and eventually crash. One solution is to cache the calculated size in your cell and invalidate this anytime you reuse the cell or change its content as I have done with the isHeightCalculated property.

体验你的布局

此时,您的collectionView中应该有“功能正常”的动态单元格。我还没有发现开箱即用的解决方案充分在我的测试,所以请随意评论,如果你有。在我看来,UITableView仍然赢得了动态大小的战斗。

##Caveats Be very mindful that if you are using prototype cells to calculate the estimatedItemSize - this will break if your XIB uses size classes. The reason for this is that when you load your cell from a XIB its size class will be configured with Undefined. This will only be broken on iOS 8 and up since on iOS 7 the size class will be loaded based on the device (iPad = Regular-Any, iPhone = Compact-Any). You can either set the estimatedItemSize without loading the XIB, or you can load the cell from the XIB, add it to the collectionView (this will set the traitCollection), perform the layout, and then remove it from the superview. Alternatively you could also make your cell override the traitCollection getter and return the appropriate traits. It's up to you.


对任何可能有帮助的人,

如果设置了estimatedItemSize,我就会发生严重的崩溃。即使我在numberOfItemsInSection中返回0。因此,单元格本身和它们的自动布局不是导致崩溃的原因……collectionView刚刚崩溃,即使是空的,这只是因为estimatedItemSize被设置为自大小。

在我的例子中,我重新组织了我的项目,从一个包含collectionView的控制器到一个collectionViewController,它工作了。

图。

在Swift 5中,它适用于我。

UICollectionViewFlowLayout:

estimatedItemSize = UICollectionViewFlowLayout.automaticSize

UICollectionViewCell:

(ps:我用的是SnapKit)

class Cell: UICollectionViewCell {
    let customizedContentView = UIView()
    ...
    func layoutAction() {
        contentView.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(customizedContentView)
        customizedContentView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
    }
}

然后你只需要展开customizedContentView。