UICollectionViewCell:AutoLayout - 尺寸计算不正确(偏移 10 pt)

Posted

技术标签:

【中文标题】UICollectionViewCell:AutoLayout - 尺寸计算不正确(偏移 10 pt)【英文标题】:UICollectionViewCell: AutoLayout - incorrect size calculation (offset by exacatly 10 pt) 【发布时间】:2018-09-27 11:36:58 【问题描述】:

我有一组具有不同布局(使用 AutoLayout)的 UICollectionViewCell 子类。

由于“Self Sizing Cells”功能完全失效,我在 DataSource 中手动计算大小。但是,我注意到无论单元格如何,计算出的大小比应有的大小正好小 10 pt

这是我用来计算大小的代码:

  let sizeCache = CellCache() // Size cache stores previously calculated values
  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize 
    if let cachedSize = sizeCache.sizeAtIndexPath(indexPath: indexPath) 
      return cachedSize // Return cached value if it exists
    

    // Calculate the size if not found in the cache
    let cellClass = cellTypeAt(indexPath: indexPath) // Get the class type
    let cell = sizeCache.createCellOfTypeIfNeeded(cellType: cellClass) // Dequeue or instantiate a cell
    self.collectionView(collectionView, cell: cell, configureAt: indexPath) // Ask DataSource to configure this cell
    let fittingSize = CGSize(width: collectionView.bounds.width - 16, height: 0) // Set the padding to 16
    cell.bounds = CGRect(origin: .zero, size: fittingSize) // Set cell's width
    var result = cell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) // Calculate cell's size using AutoLayout
    result.width = collectionView.bounds.width - 16 // Set the padding
    result.height = result.height + 10 // !!! Error - Add 10 pt to the result !!!
    sizeCache.setSize(size: result, at: indexPath) // Cache the result
    return result
  

请注意末尾的行,我必须添加 10 pt 才能使其正常工作。

我所有的单元格都是这个基类的子类,它设置了width 约束:

import UIKit
import SnapKit

class AutoSizingCellBase: UICollectionViewCell 
  override class var requiresConstraintBasedLayout: Bool 
    return true
  

  private final var widthConstraint: Constraint?

  override func updateConstraints() 
    if widthConstraint == nil 
      if let window = window 
        let width = window.bounds.width - 16
        contentView.snp.makeConstraints  (make) in
          widthConstraint = make.width.equalTo(width).constraint
        
      
    
    super.updateConstraints()
  

问题的例子:

正确的大小(添加 10 pt 后)

尺寸不正确(未添加 10 pt)

此问题会影响所有单元格。它的根本原因是什么?

更新:约束示例 以下是我用来配置所示视图的约束:

  private func setupConstraints() 
    price.setContentCompressionResistancePriority(.required, for: .horizontal)
    disclosureIndicator.setContentCompressionResistancePriority(.required, for: .horizontal)
    disclosureIndicator.setContentCompressionResistancePriority(.required, for: .vertical)
    disclosureIndicator.setContentHuggingPriority(.required, for: .horizontal)
    disclosureIndicator.setContentHuggingPriority(.required, for: .vertical)

    title.snp.makeConstraints  (make) in
      make.leading.top.equalTo(contentView.layoutMarginsGuide)
      make.trailing.lessThanOrEqualTo(price.snp.leading).offset(-8)
    

    subtitle.snp.makeConstraints  (make) in
      make.top.equalTo(title.snp.bottom).offset(8)
      make.leading.bottom.equalTo(contentView.layoutMarginsGuide)
    

    disclosureIndicator.snp.makeConstraints  (make) in
      make.trailing.equalTo(contentView.layoutMarginsGuide)
      make.centerY.equalToSuperview()
    

    price.snp.makeConstraints  (make) in
      make.trailing.equalTo(disclosureIndicator.snp.leading).offset(-8)
      make.centerY.equalToSuperview()
    
  

这是一个相当复杂的示例,但即使使用更简单的示例,问题也可以重现:

  private func setupConstraints() 
    button.snp.makeConstraints  (make) in
      make.width.equalToSuperview() // Button is a subview of the UIStackView
    

    stack.snp.makeConstraints  (make) in
      make.edges.equalTo(contentView.layoutMarginsGuide)
      make.height.greaterThanOrEqualTo(150)
    
  

更新

在约束中添加偏移解决了这个问题,而视图层次调试器仍然显示“不明确的布局”:

subtitle.snp.makeConstraints  (make) in
  make.top.equalTo(title.snp.bottom).offset(8)
  make.leading.equalTo(contentView.layoutMarginsGuide)
  make.bottom.equalTo(contentView.layoutMarginsGuide).offset(-10) // Subtracted 10

问题是10 来自哪里仍然是开放的。

更新 3 似乎部分问题在于调整layoutMargins

UIView.appearance().layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)

AppDelegate 中删除上面的行后,大小计算变得正确,尽管单元格的外观发生了变化。 所以我认为问题是由于某种原因,为调整大小而创建的单元格的插图与从UICollectionView 出列的单元格的插图不同。

【问题讨论】:

取决于您的约束条件。 另外,这可以用表格视图代替吗? @matt 我已经用约束示例更新了我的问题。 @matt UItableView 在这里不是一个选项,因为 UICollectionView 具有带有阴影、标题和分隔符的自定义布局。 那么问题来了。正如我所建议的,这是约束。 【参考方案1】:

UICollectionViewCell 添加到父视图和不在视图层次结构中时,LayoutMargins 的计算方式不同。

因为线:

UIView.appearance().layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)

添加到超级视图的单元格与为调整大小而创建的单元格具有不同的边距。因此有区别。

【讨论】:

以上是关于UICollectionViewCell:AutoLayout - 尺寸计算不正确(偏移 10 pt)的主要内容,如果未能解决你的问题,请参考以下文章

UICollectionViewCell与UIScrollView取消didSelectItemAtIndexPath

UICollectionViewCell 子类问题

重用 UICollectionViewCell

UICollectionViewCell 中的 Outlet 为零

UICollectionViewCell 边框/阴影

用 nib 定义 UICollectionViewCell