UICollectionView 具有自动调整单元格(estimatedSize)和 sectionHeadersPinToVisibleBounds 的精神
Posted
技术标签:
【中文标题】UICollectionView 具有自动调整单元格(estimatedSize)和 sectionHeadersPinToVisibleBounds 的精神【英文标题】:UICollectionView with autosizing cell (estimatedSize) and sectionHeadersPinToVisibleBounds goes mental 【发布时间】:2017-01-27 15:24:23 【问题描述】:考虑以下情况。我有一个UICollectionView
(在UICollectionViewController
内),它看起来与UITableView
几乎相同(我不使用UITalbeView
的原因是因为我在布局上有非数据视图,这是我不想要的管理和弄乱我的IndexPath
)。
为了实现自动调整单元格大小,我设置了estimatedItemSize
,如下所示:
layout.estimatedItemSize = CGSize(width: self.view.bounds.size.width, height: 72)
另外,在我的单元格中,我有布局属性:
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes
layoutAttributes.bounds.size.height = systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
return layoutAttributes
因此,通过这样做,我得到了与 UITableView
一样的精确布局,并具有自动调整大小。而且效果很好。
现在,我正在尝试添加标题并将其固定在滚动到该部分的顶部,如下所示:
layout.sectionHeadersPinToVisibleBounds = false
但是布局进入了奇怪的状态,我到处都有小故障,单元格相互重叠,并且标题有时不粘。
更新:
view controller和cell的代码:
class ViewController: UICollectionViewController
override func viewDidLoad()
super.viewDidLoad()
let layout = collectionView?.collectionViewLayout as! UICollectionViewFlowLayout
layout.sectionHeadersPinToVisibleBounds = true
layout.estimatedItemSize = CGSize(width: collectionView?.bounds.size.width ?? 0, height: 36) // enables dynamic height
override func numberOfSections(in collectionView: UICollectionView) -> Int
return 10
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return 10
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CustomCell
cell.heightConstraint.constant = CGFloat(indexPath.row * 10 % 100) + 10 // Random constraint to make dynamic height work
return cell
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView
return collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "Header", for: indexPath)
class CustomCell : UICollectionViewCell
let identifier = "CustomCell"
@IBOutlet weak var rectangle: UIView!
@IBOutlet weak var heightConstraint: NSLayoutConstraint!
override func awakeFromNib()
translatesAutoresizingMaskIntoConstraints = false
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes
layoutAttributes.bounds.size.height = systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
return layoutAttributes
视频滞后详情:https://vimeo.com/203284395
【问题讨论】:
添加一些图片以供参考将有助于我们可视化您遇到的问题 能否附上截图? 我没有答案,但部分问题可能是您基于self.view.bounds.size.width
设置layout.estimatedItemSize
。如果您使用自动布局,那么width
有效的地方是违反直觉的。你所做的基本上是递归的:你根据width
设置一些东西,然后导致视图被调整大小,这导致width
发生变化。尝试删除该循环依赖。
不,这是不对的。边界宽度与单元格布局无关。布局只影响单元格的大小和位置,而不影响集合视图本身的大小和位置。另外, self.view 是这里的集合视图的超级视图,所以根本不重要。它只是一个常数。
@PavelGatilov 添加您的 UI 图像和一些代码工作您所拥有的,以便我们可以准确估计需求并为您提供最佳答案,否则答案将是假设。
【参考方案1】:
WWDC 2017 更新:
我的同事在 WWDC 2017 上,他向一位 UIKit 工程师询问了这个问题。工程师确认这个问题是 Apple 已知的 bug,目前还没有修复。
【讨论】:
这个有什么新东西吗?【参考方案2】:使用UICollectionViewDelegateFlowLayout
方法。
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
//Calculate your Dynamic Size Here for Each Cell at SpecificIndexPath.
//For Example You want your Cell Height to be dynamic with Respect to indexPath.row number
let cellWidth = collectionView?.bounds.size.width
//Now Simply return these CellWidth and Height, and you are Good to go.
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCellIdentifier", for: indexPath)
//You Can Pass TextField As Well as i have considered UITextView for now.
let cellSize = self. calculateSize(cellTextView: cell.textView, cellText: yourArrayOfText[indexPath.row], withFixedWidth: cellWidth)
return cellSize
并且不要通过直接更改 Constraint.constant 来更改 Cell Height。而不是这个简单地使用 Above Delegate 方法来改变高度。更改单元约束可能会导致此类问题。
使用下面的方法得到你想要的尺寸。
func calculateSize(cellTextView: UITextView, cellText: String, withFixedWidth: CGFloat) -> CGSize
let textView = UITextView()
textView.text = cellText
textView.frame = cellTextView.frame
textView.font = cellTextView.font
textView.tag = cellTextView.tag
let fixedWidth = withFixedWidth
textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
var newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
return newSize
使用 Bellow 方法计算图像大小。
//Method to Calculate ImageSize.
func calculateImageSize(image: UIImage) -> CGSize
var newSize = image.size
if newSize.width > (ChatView.maxWidth - 70)
newSize.width = ChatView.maxWidth - 70
newSize.height = ChatView.maxWidth - 70
if newSize.height < 60
newSize.height = 60
newSize.width = 60
return newSize
【讨论】:
好的。你不明白。约束只代表逻辑的一部分。它可以由任何动态的东西驱动,比如标签或图像中的文本数量等。 所以你想改变 Cell with Respect 的大小到里面的文字?? 不一定是文本,但是是的,背后的想法是根据内部内容的大小更改单元格大小。当我添加粘贴标题时,一切都出错了。 现在检查我的答案。我已经制定了一种方法来获取 TextView 相对于文本的大小。逻辑是创建一个空单元格并将 Cell 的 textView 沿文本和宽度传递给方法。现在您可以创建一个新的 textView 并将字体大小、字体类型分配给单元格的 textView 到这个新的 textView 并计算高度。现在只需将该高度和固定宽度返回给 UICollectionViewDelegateFlowLayout 方法。您现在将了解如何执行此操作。 我已经附上了计算图像大小的方法。以上是关于UICollectionView 具有自动调整单元格(estimatedSize)和 sectionHeadersPinToVisibleBounds 的精神的主要内容,如果未能解决你的问题,请参考以下文章
UICollectionView 自定义布局动画故障,在 performBatchUpdates 上具有自我调整单元格大小
在 UICollectionView 中自动调整单元格(全部在代码中)
UICollectionView 具有自调整大小的单元格未正确计算 contentSize
设备旋转时如何自动调整 UICollectionView 的大小?