【问题标题】:Pin Cells to Top of UICollectionView Edge将单元格固定到 UICollectionView 边缘的顶部
【发布时间】:2021-08-28 23:28:12
【问题描述】:

我有一个水平滚动的uicollectionview。我根据subviewuilabel 文本字符串的长度动态更改每个单元格的高度。最后,我将uicollectionview 高度约束设置为等于最长的单元格高度。

这会导致uicollectionview,其中每个单元格的高度各不相同,并阻止我实现我的目标:让所有单元格的顶部对齐并固定到uicollectionview 的顶部边缘。

这里有人可以帮忙吗?我知道我可能必须为此创建一个自定义布局,但不知道从哪里开始,也找不到任何指导参考。任何指导将不胜感激!

【问题讨论】:

  • 显示您当前获得的图像,与您想要获得的相比。听起来您应该将单元格的内容元素限制在单元格的顶部,并在底部留出空白空间。
  • @DonMag 为清晰起见添加了一张图片!
  • 好的 - 所以,让所有单元格的高度相同,并将它们的 UI 元素限制在单元格 contentView 的顶部 - 完成!
  • @DonMag 为我做的,谢谢。

标签: ios swift uicollectionview uicollectionviewcell uicollectionviewlayout


【解决方案1】:

使用自定义 UICollectionViewLayout 很容易做到这一点,

简单地说:

您只需手动分配每个项目的框架。

注意func prepare() 中的逻辑

其余方法只是普通模式。

对于可变项目高度,您可以使用委托来获取值。

enum CommonComponent: String {
  case cell
  case header
  
  var kind: String {
      switch self{
      case .header:
          return "UICollectionElementKindSectionHeader"
      default:
          return ""
      }
  }
}


protocol CustomLayoutProxy: AnyObject {
    func getHeightFor(indexPath ip: IndexPath) -> CGFloat
}



class CustomLayout: UICollectionViewLayout {
    
  weak var delegate: CustomLayoutProxy?
    
  override public var collectionViewContentSize: CGSize {
     CGSize(width: collectionViewWidth, height: contentHeight)
  }
  
  // MARK: - Properties

  private var contentHeight = CGFloat.zero

  private var cache = [CommonComponent: [IndexPath: UICollectionViewLayoutAttributes]]()
    
    
  private var visibleLayoutAttributes = [UICollectionViewLayoutAttributes]()
  
  private var collectionViewHeight: CGFloat {
     collectionView!.frame.height
  }
  
  private var collectionViewWidth: CGFloat {
     collectionView!.frame.width
  }

    let maxItemSize = CGSize(width: 40, height: 40)
}


// MARK: - LAYOUT CORE PROCESS
extension CustomLayout{
  
  override public func prepare() {
         guard let collectionView = collectionView else {
             return
         }
        
         prepareCache()
     
        var cellX: CGFloat = 16
        
        let countOne = collectionView.numberOfItems(inSection: 0)

         let headerIP = IndexPath(item: 0, section: 0)
         let headH: CGFloat = 58
         let headerAttributes = UICollectionViewLayoutAttributes(forSupplementaryViewOfKind: CommonComponent.header.kind, with: headerIP)
         headerAttributes.frame = CGRect(x: 0, y: contentHeight, width: UI.std.width, height: headH)
         cache[.header]?[headerIP] = headerAttributes
        
        contentHeight += headH
    
        for item in 0 ..< countOne{
              let cellIndexPath = IndexPath(item: item, section: 0)
              let attributes = UICollectionViewLayoutAttributes(forCellWith: cellIndexPath)
            
              var size = maxItemSize
              if let h = delegate?.getHeightFor(indexPath: cellIndexPath){
                  size.height = h
              }
              attributes.frame = CGRect( x: cellX, y: contentHeight, width: size.width, height: size.height )
              contentHeight += exceedUp(origin: &cellX, limit: collectionViewWidth)
              cache[.cell]?[cellIndexPath] = attributes
            
        }
        contentHeight += (maxItemSize.height + 16)
   }

    private func prepareCache() {
        contentHeight = 0
        cache.removeAll(keepingCapacity: true)
        cache[.cell] = [IndexPath: UICollectionViewLayoutAttributes]()
        cache[.header] = [IndexPath: UICollectionViewLayoutAttributes]()
    }
}


//MARK: - PROVIDING ATTRIBUTES TO THE COLLECTIONVIEW
extension CustomLayout{
  
    
    public override func layoutAttributesForSupplementaryView(ofKind ComponentKind: String, at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
          switch ComponentKind {
          case CommonComponent.header.kind:
            return cache[.header]?[indexPath]
          default:
            return nil
          }
    }
    
    override public func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
            return cache[.cell]?[indexPath]
    }

    
    override public func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
          guard let _ = collectionView else { return nil }
          visibleLayoutAttributes.removeAll(keepingCapacity: true)
          for (_, infos) in cache {
            for (_, attributes) in infos {
              attributes.transform = .identity
              if attributes.frame.intersects(rect) {
                  visibleLayoutAttributes.append(attributes)
              }
            }
          }
          return visibleLayoutAttributes
    }

    
    func exceedUp(origin x: inout CGFloat, limit widthX: CGFloat) -> CGFloat{
        var exceedH: CGFloat = 0
        let lineSpacing: CGFloat = 20
        let interitemSpacing: CGFloat = 16
        let contentEdge = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16)
        if x + maxItemSize.width * 2 + interitemSpacing + contentEdge.right < widthX + 1{
            x += maxItemSize.width + interitemSpacing
       }
       else{
           x = contentEdge.left
           exceedH = (maxItemSize.height + lineSpacing)
       }
       return exceedH
    }
}

更多例子,你可以查看我的github repo

【讨论】:

    猜你喜欢
    • 2017-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-07
    • 1970-01-01
    • 1970-01-01
    • 2019-08-08
    • 2014-10-21
    相关资源
    最近更新 更多