这个答案在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.