UICollectionView 05 - 可伸缩Header

文章按照顺序写的,之前文章写过的很多逻辑都会略过,建议顺序阅读,并下载源码结合阅读。

目录

项目下载地址: CollectionView-Note

UICollectionView 01 - 基础布局篇
UICollectionView 02 - 布局和代理篇
UICollectionView 03 - 自定义布局原理篇
UICollectionView 04 - 卡片布局
UICollectionView 05 - 可伸缩Header
UICollectionView 06 - 瀑布流布局
UICollectionView 07 - 标签布局

上一篇 通过 UICollectionViewFlowLayout ,然后对其cell实时监控根据距离中心位置进行缩放实现了卡片布局 , 这篇继续继承 UICollectionViewFlowLayout 对其SupplementaryView 做一些小处理 ,即可实现可伸缩头部。

继续在 Storyboard 中拖出一个 ViewController , 然后放上一个 UICollectionView . 依旧使用之前篇幅的色块作为cell,这里就放几个横条cell (不关键,本篇主要针对 SupplementaryView)。 然后新建一个继承自UICollectionReusableViewImageHeaderView . 这边使用xib创建的 也可以直接再 Storyboard中或者纯代码 。 ImageView 是靠四个边的。

class ImageHeaderView: UICollectionReusableView {
  
  static let reuseID = "ImageHeaderView"
  
  @IBOutlet weak var imageView: UIImageView!
  
  override func awakeFromNib() {
    super.awakeFromNib()
    imageView.contentMode = .scaleAspectFill
    imageView.clipsToBounds = true
  }
  
}

整个 ViewController 大概就这样


class StretchyHeaderViewController: UIViewController {
  
  @IBOutlet weak var collectionView: UICollectionView!
  
  var layout: UICollectionViewFlowLayout? {
    return collectionView.collectionViewLayout as? UICollectionViewFlowLayout
  }
  
  var colors: [UIColor] = []
  override func viewDidLoad() {
    super.viewDidLoad()
    
    colors = DataManager.shared.generalColors(3)
    collectionView.dataSource = self
    collectionView.register(UINib(nibName: "ImageHeaderView", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: ImageHeaderView.reuseID)
    
    collectionView.register(UINib(nibName: "BasicsHeaderView", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: BasicsHeaderView.reuseID)
    
    collectionView.alwaysBounceVertical = true
    let width = view.bounds.width
    layout?.itemSize = CGSize(width: width, height: 50)
    layout?.minimumLineSpacing = 2
    layout?.headerReferenceSize = CGSize(width: width, height: 150)
  }
  
}


// MARK: - UICollectionViewDataSource

extension StretchyHeaderViewController: UICollectionViewDataSource {
  
  func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return colors.count
  }
  
  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BasicsCell.reuseID, for: indexPath) as! BasicsCell
    cell.backgroundColor = colors[indexPath.row]
    return cell
  }
  
  func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    switch kind {
    case UICollectionView.elementKindSectionHeader:
      let view = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: ImageHeaderView.reuseID, for: indexPath) as! ImageHeaderView
      return view
    default:
      fatalError("No such kind")
    }
  }
  
}

一个很普通的header + cell的布局, 如下,拖动的时候没有任何效果。

000

下面我们自定义一个继承自 UICollectionViewFlowLayoutStretchyLayout .

跟上篇一样重写override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? 方法。

override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    // 1
    guard let collectionView = self.collectionView else { return nil }
    guard let attributes = super.layoutAttributesForElements(in: rect) else { return nil }

    // 2
    let insets = collectionView.contentInset
    let offset = collectionView.contentOffset
    let minY = -insets.top
    
    // 3
    if offset.y < minY {
      // 4
      let headerSize = self.headerReferenceSize
      let deltalY = abs(offset.y - minY)
      for attibute in attributes {
        // 5 
        if attibute.representedElementKind == UICollectionView.elementKindSectionHeader {
          // 6 
          var headerRect = attibute.frame
          headerRect.size.height = headerSize.height + deltalY
          headerRect.origin.y = headerRect.origin.y - deltalY
          attibute.frame = headerRect
        }
      }
    }
    return attributes
  }

这里解释下

  1. 获取collectionView 和 父类返回的 attributes
  2. 我们的拉伸是从顶部开始拉伸,所以这里获取到当前的 offset 和顶部的 inset
  3. 如果滚动的y坐标比顶部最小的inset还小,就需要拉伸了
  4. 获取到headersize,以及我们滚动超过顶部的距离 deltalY
  5. 遍历 attributes ,我们只针对Section Header 进行处理
  6. 获取到header的frame,根据滚动的距离 ,调整header的大小和坐标。

最后 跟上一篇一样,当bounds改变时重新计算。

override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
    return true
}

ok,其实没啥复杂的东西,然后再storyboard中将layout设置为我们自定义的layout (不会设置的看上一篇)。 ViewController 中获取到的也是我们的 StretchyLayout

var layout: StretchyLayout? {
    return collectionView.collectionViewLayout as? StretchyLayout
}

再次运行。


000
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明AI阅读 16,032评论 3 119
  • 君子兰近来读到汪曾祺的《滇游新记》,里面有定市花的情节,不由想起了老家的几盆君子兰——长春市市花。在吉林省,但凡家...
    高卢的林川迪斯阅读 405评论 0 0
  • —余倩 夜幕降临,进入大二的我结束了一天的课程,洗去了一身的疲倦进去了梦乡…… 梦里一位十六七岁的花季女孩,穿着朴...
    365读书计划阅读 368评论 0 2
  • 我陈庚下定决心,公众承诺,必须彻底改变,让我的生命从此再也不一样。我知道,所有的新结果,来自新的习惯建立,因此,我...
    陈庚新生阅读 197评论 0 0