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的布局, 如下,拖动的时候没有任何效果。

UICollectionView 05 - 可伸缩Header_第1张图片
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
}

再次运行。


UICollectionView 05 - 可伸缩Header_第2张图片
000

你可能感兴趣的:(UICollectionView 05 - 可伸缩Header)