【问题标题】:How to add HeaderView in UICollectionView like UITableView's tableHeaderView如何像 UITableView 的 tableHeaderView 一样在 UICollectionView 中添加 HeaderView
【发布时间】:2012-11-11 09:43:10
【问题描述】:

如何在UICollectionView 的顶部添加标题视图/顶视图(不是部分标题)?

它应该与UITableViewtableHeaderView 属性完全相同。

因此它需要位于第一个部分标题视图的顶部(在索引 0 的部分之前),与其余内容一起滚动,并进行用户交互。

到目前为止,我想出的最好的方法是为第一个部分标题视图制作一个特殊的 XIB(UICollectionReusableViewMyCollectionReusableView 子类作为文件的所有者),该视图大到足以包含我的子视图标头,我认为这是一种 hack,我还没有设法检测到触摸。

不确定我是否可以让我的 MyCollectionReusableView 子类允许触摸或有更好的方法。

【问题讨论】:

  • 您找到解决这种情况的方法了吗?我也有同样的问题

标签: ios objective-c swift uitableview uicollectionview


【解决方案1】:
self.collectionView  =  [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 320, self.view.frame.size.height) collectionViewLayout:flowlayout];
self.collectionView.contentInset = UIEdgeInsetsMake(50, 0, 0, 0);
UIImageView *imagev = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"015.png"]];
imagev.frame = CGRectMake(0, -50, 320, 50);
[self.collectionView addSubview: imagev];
[self.view addSubview: _collectionView];

我使用属性contentInsetUICollectionView的顶部插入一个框架,然后我将图像视图添加到它,它成功了。我认为它可以完全充当UITableViewtableHeaderView 属性。你怎么看?

【讨论】:

  • 我在找这个,比把第一节的标题变高好多了。
  • 我已经使用上述方式添加标题 vie ,无法在标题视图上获得触摸事件,知道为什么吗?
  • 谢谢你。
  • @arpan_techisavy 因为您添加的视图超出了它的超级视图。
  • 有什么方法可以添加collectionFooterView?
【解决方案2】:

我通过添加 UIView 作为集合视图的子视图,将视图置于集合视图的顶部。它确实与集合视图一起向上滚动,并且具有正常的 UIView 用户交互。如果您没有节标题,这可以正常工作,但如果您这样做则不起作用。在那种情况下,我认为你的做法没有任何问题。我不确定你为什么没有检测到触摸,它们对我来说很好。

【讨论】:

  • 谢谢,我已经使用了节标题,所以第一个(索引 0 处的节标题)我已经做得更大,以包含标准节标题和集合视图顶部的视图。但是你说你接触到了你的MyCollectionReusableView 子类?
  • 刚回去再试一次,你是对的——它有效!我工作太晚了,我使用UIImageView's 来检测触摸并忘记在 Xcode 中为我的图像视图勾选“启用用户交互”。
  • 您是如何做到这一点的?我将它(它是一个UISearchBar btw)添加到带有所需 contentInset 的 collectionView (带有约束顶部,前导尾随),它只是在顶部显示一个空白区域,在视图调试器中它说集合视图内容大小不明确。
  • 我真的被困在这里了。如果您能帮助我,我将不胜感激。
【解决方案3】:

我最终使用 UICollectionView 扩展来添加我的自定义标题。使用特定标签,我可以伪造存储的属性来识别我的自定义标头(从外部透明)。 如果在添加自定义标题时 UICollectionView 的布局未完成,您可能必须滚动到特定偏移量。

extension UICollectionView {
    var currentCustomHeaderView: UIView? {
        return self.viewWithTag(CustomCollectionViewHeaderTag)
    }

    func asssignCustomHeaderView(headerView: UIView, sideMarginInsets: CGFloat = 0) {
        guard self.viewWithTag(CustomCollectionViewHeaderTag) == nil else {
            return
        }
        let height = headerView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height
        headerView.frame = CGRect(x: sideMarginInsets, y: -height + self.contentInset.top, width: self.frame.width - (2 * sideMarginInsets), height: height)
        headerView.tag = CustomCollectionViewHeaderTag
        self.addSubview(headerView)
        self.contentInset = UIEdgeInsets(top: height, left: self.contentInset.left, bottom: self.contentInset.bottom, right: self.contentInset.right)
    }

    func removeCustomHeaderView() {
        if let customHeaderView = self.viewWithTag(CustomCollectionViewHeaderTag) {
            let headerHeight = customHeaderView.frame.height
            customHeaderView.removeFromSuperview()
            self.contentInset = UIEdgeInsets(top: self.contentInset.top - headerHeight, left: self.contentInset.left, bottom: self.contentInset.bottom, right: self.contentInset.right)
        } 
    }
}

CustomCollectionViewHeaderTag 是指您提供给标题的标签号。确保它不是嵌入在 UICollectionView 中的 anotherView 的标签。

【讨论】:

  • 这确实是解决节标题冲突的好方法!
  • 这真的,真的,真的帮助了我。惊人的。谢谢!
【解决方案4】:

从 iOS 13 开始,设置标头的规范方法是使用 UICollectionViewCompositionalLayoutConfiguration

这种方式允许为每个部分设置 boundarySupplementaryItems 或为集合视图全局设置。

let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .estimated(44))

let header = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize, elementKind: "header", alignment: .top)

let config = NSCollectionViewCompositionalLayoutConfiguration()
config.boundarySupplementaryItems = [header]

let layout = UICollectionViewCompositionalLayout(sectionProvider: sectionProvider, configuration: config)

return layout

要了解更多信息,请访问官方文档:https://developer.apple.com/documentation/appkit/nscollectionviewcompositionallayoutconfiguration

【讨论】:

    【解决方案5】:

    实现这一点的最佳方法是为第一节制作节标题。然后设置 UICollectionViewFlowLayout 的属性:

    @property(nonatomic) BOOL sectionHeadersPinToVisibleBounds
    

    或快速

    var sectionHeadersPinToVisibleBounds: Bool
    

    为 NO(快速为 false)。这将确保节标题与单元格一起连续滚动,并且不会像节标题通常那样固定在顶部。

    【讨论】:

    • 这对我有用,但是如果我想要一些 sectionHeader 固定在顶部,但有些不呢?这个 UICollectionViewFlowLayout 的属性就像一个全局集。
    【解决方案6】:

    我认为最好的方法是将 UICollectionViewLayout(UICollectionViewFlowLayout) 子类化并添加所需的页眉或页脚属性。

    由于 UICollectionView 布局中包括补充视图在内的所有项目都根据其collectionViewLayout 属性,所以由collectionviewlayout 决定是否存在页眉(页脚)。

    使用contentInset 更容易,但是当您想要动态隐藏或显示标题时可能会出现问题。

    我已经编写了一个协议来实现这一点,它可以被 UICollectionViewLayout 的任何子类采用,以使其具有页眉或页脚。你可以找到它here.

    主要思想是为标题创建一个UICollectionViewLayoutAttributes 并在需要时在layoutAttributesForElements(in rect: CGRect) 中返回它,您将不得不修改所有其他属性,它们的所有位置都应该向下移动标题高度,因为标题高于一切。

    【讨论】:

      【解决方案7】:
      private var PreviousInsetKey: Void?
      extension UIView {
      
      var previousInset:CGFloat {
          get {
              return objc_getAssociatedObject(self, &PreviousInsetKey) as? CGFloat ?? 0.0
          }
          set {
              if newValue == -1{
                  objc_setAssociatedObject(self, &PreviousInsetKey, nil, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
              }
              else{
                  objc_setAssociatedObject(self, &PreviousInsetKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
              }
          }
      }
      
      
      func addOnCollectionView(cv: UICollectionView){
          if self.superview == nil{
              var frame = self.frame
              frame.size.width = SCREEN_WIDTH
              cv.frame = frame
              self.addOnView(view: cv)
              let flow = cv.collectionViewLayout as! UICollectionViewFlowLayout
              var inset = flow.sectionInset
              previousInset = inset.top
              inset.top = frame.size.height
              flow.sectionInset = inset
          }
      
      }
      
      func removeFromCollectionView(cv: UICollectionView){
          if self.superview == nil{
              return
          }
          self.removeFromSuperview()
          let flow = cv.collectionViewLayout as! UICollectionViewFlowLayout
          var inset = flow.sectionInset
          inset.top = previousInset
          flow.sectionInset = inset
          previousInset = -1
      }
      
      func addOnView(view: UIView){
      
          view.addSubview(self)
      
          self.translatesAutoresizingMaskIntoConstraints = false
          let leftConstraint = NSLayoutConstraint(item: self, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0)
          let widthConstraint = NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: view.frame.size.width)
          let heightConstraint = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1.0, constant: view.frame.size.height)
          let topConstraint = NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 0)
      
          view.addConstraints([leftConstraint, widthConstraint, heightConstraint, topConstraint])
          self.layoutIfNeeded()
          view.layoutIfNeeded()
      }
      }
      

      【讨论】:

      • 您能解释一下您的解决方案在做什么吗?
      猜你喜欢
      • 2013-09-21
      • 1970-01-01
      • 2015-05-21
      • 2011-07-23
      • 2012-01-09
      • 1970-01-01
      • 2021-10-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多