滚动时任意更改粘性UICollectionView标头的高度

时间:2017-12-20 02:55:50

标签: ios swift uicollectionview uicollectionviewlayout

我正在尝试创建一个复杂的粘性UICollectionView标头:

  • 必须根据特定的滚动条件调整大小,例如:当用户滚动到某个y位置时,标题会调整大小。
  • 在集合视图内容滚动时必须自动调整大小
  • 它必须响应标题本身上的触摸事件(它不能像“标题”那样“出现”背景视图)

现在事实证明这是一项挑战,但我取得了一些进展。为了简化问题,我首先创建一个标题,它将在滚动时调整大小并在触摸结束时保持其边界。

首先,我以最简单的形式创建了一个集合视图和补充视图标题:

private let defaultHeaderViewHeight: CGFloat = 250.0
private var beginHeaderViewHeight: CGFloat = defaultHeaderViewHeight
private var currentHeaderViewHeight: CGFloat = defaultHeaderViewHeight

private var headerView: HeaderView? {
    get {
        guard let headerView = collectionView?.supplementaryView(forElementKind: UICollectionElementKindSectionHeader, at: IndexPath(row: 0, section: 0)) as? HeaderView else { return nil }
        return headerView
    }
}

// Resizing logic

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    // Define the header size. This is called each time the layout is invalidated, and the beginHeaderViewHeight value is different each time
    print("Header ref size delegate called, setting height to \(beginHeaderViewHeight)")
    return CGSize(width: collectionView.frame.size.width, height: beginHeaderViewHeight)
    //                                                            ^ super important!
}

override func scrollViewDidScroll(_ scrollView: UIScrollView) {

    currentHeaderViewHeight = max(0, -(collectionView?.contentOffset.y)! + beginHeaderViewHeight)

    headerView?.frame.size.height = currentHeaderViewHeight
    headerView?.frame.origin.y = collectionView?.contentOffset.y ?? 0

    print("Did scroll\t\t\tcurrentHeaderViewHeight: \(currentHeaderViewHeight), content offset: \(scrollView.contentOffset.y)")
}

override func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

    print("Will end dragging\t\theader view frame height: \(headerView?.frame.size.height), content offset: \(scrollView.contentOffset.y)")

    scrollView.bounds.origin = CGPoint(x: 0, y: 0)

    self.collectionView?.collectionViewLayout.invalidateLayout()
    print("Will end dragging\t\theader view frame height: \(headerView?.frame.size.height), content offset: \(scrollView.contentOffset.y)")
    beginHeaderViewHeight = currentHeaderViewHeight

}

// Basic UICollectionView setup

override func viewDidLoad() {
    super.viewDidLoad()

    collectionView?.register(UINib(nibName: "HeaderView", bundle: nil), forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "Header View")

}

override func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
}

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return 50
}

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
    return cell
}

这种方法有效,但并非完全如此。当我向上滚动(触摸轨道向下)时,它的行为与我期望的一样 - 当触摸结束时,标题会调整大小并固定到位。这是一个用于验证的日志提取:

Did scroll          currentHeaderViewHeight: 447.333333333333, content offset: -70.3333333333333
Did scroll          currentHeaderViewHeight: 449.0, content offset: -72.0
Did scroll          currentHeaderViewHeight: 451.0, content offset: -74.0
Did scroll          currentHeaderViewHeight: 451.666666666667, content offset: -74.6666666666667
Will end dragging   header view frame height: Optional(451.666666666667), content offset: -74.6666666666667
Will end dragging   header view frame height: Optional(451.666666666667), content offset: 0.0
Did end dragging    currentHeaderViewHeight: 451.666666666667, content offset: 0.0
Header ref size delegate called, setting height to 451.666666666667

但是,当我向下滚动(触摸向上)时,它会变得非常糟糕:看起来scrollViewDidScroll被调用,even though I'm not setting the content offset anywhere

Did scroll          currentHeaderViewHeight: 330.666666666667, content offset: 121.0
Did scroll          currentHeaderViewHeight: 330.333333333333, content offset: 121.333333333333
Did scroll          currentHeaderViewHeight: 330.0, content offset: 121.666666666667
Did scroll          currentHeaderViewHeight: 328.333333333333, content offset: 123.333333333333
Will end dragging   header view frame height: Optional(328.333333333333), content offset: 123.333333333333
Will end dragging   header view frame height: Optional(328.333333333333), content offset: 0.0
Did end dragging    currentHeaderViewHeight: 328.333333333333, content offset: 0.0
Header ref size delegate called, setting height to 328.333333333333
// WHY ARE THESE LAST TWO LINES CALLED?
Did scroll          currentHeaderViewHeight: 205.0, content offset: 123.333333333333
Did end deceler..   currentHeaderViewHeight: 205.0, content offset: 123.333333333333

所以tl;博士在这里:为什么当我向一个方向而不是另一个方向滚动时它是否有效?

奖励积分:如果有更清洁,更简单的方法来完成所有这些,请随时分享!

0 个答案:

没有答案