UICollectionView 的弹性标题 [英] Stretchy Header for UICollectionView

查看:23
本文介绍了UICollectionView 的弹性标题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 collectionview 部分标题创建一个有弹性的标题.有很多不同的方法可以做到这一点,但我只是在寻找最简单、最直接的方法,并清楚地说明要做什么.有没有人看过在 Swift 3 中执行此操作的简单指南,或者您能否解释一下如何在此处完成?

我觉得应该没那么难.我想使用 UICollectionViewDelegateFlowLayoutScrollviewDelegate 因为我认为这是最简单的方法.

我可以在用户滚动时使用 scrollViewDidScroll 方法来控制标题的高度吗?如何手动更改标题的高度?我知道在情节提要上我可以在 UICollectionView 标题大小设置中更改它,但我将如何在代码中调整它?

解决方案

使用 Swift 5/iOS 12.3,您可以覆盖

I am trying to create a stretchy header using a collectionview section header. There are a bunch of different ways to do this but I am just looking for the simplest and most straight forward method with clear insturctions on what to do. Has anybody seen a simple guide on doing this in Swift 3 or can you explain how this would be done here?

I don't think it should be that difficult. I would like to use the UICollectionViewDelegateFlowLayout and the ScrollviewDelegate becuase I think that would be the easiest way for it to be done.

Can I use the scrollViewDidScroll method to control height of the header as a user is scrolling. How would I change the height of the header manually? I know on the storyboard I can change it in the UICollectionView header size settings but how would I adjust it in code?

解决方案

With Swift 5 / iOS 12.3, you can override shouldInvalidateLayout(forBoundsChange:) and layoutAttributesForElements(in:) methods inside a UICollectionViewFlowLayout subclass in order to create a stretchy header in a UICollectionView. The following sample code shows how to implement those methods:

CollectionViewController.swift

import UIKit

class CollectionViewController: UICollectionViewController {

    let flowLayout = CustomFlowLayout()

    override func viewDidLoad() {
        super.viewDidLoad()

        guard let collectionView = collectionView else { fatalError() }

        collectionView.alwaysBounceVertical = true
        collectionView.contentInsetAdjustmentBehavior = .always
        collectionView.collectionViewLayout = flowLayout
        collectionView.register(CollectionViewCell.self, forCellWithReuseIdentifier: "Cell")
        collectionView.register(HeaderReusableView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "HeaderView")
    }

    override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "HeaderView", for: indexPath) as! HeaderReusableView
        return headerView
    }

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

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        return collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
    }

}

CustomFlowLayout.swift

import UIKit

class CustomFlowLayout: UICollectionViewFlowLayout {

    let idealCellWidth: CGFloat = 100
    let margin: CGFloat = 10

    override init() {
        super.init()

        sectionInsetReference = .fromSafeArea
        sectionInset = UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin)
        headerReferenceSize = CGSize(width: 0, height: 80)
        sectionHeadersPinToVisibleBounds = false
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func prepare() {
        super.prepare()

        guard let collectionView = collectionView else { return }
        let availableWidth = collectionView.frame.width - collectionView.safeAreaInsets.left - collectionView.safeAreaInsets.right - sectionInset.left - sectionInset.right
        let idealNumberOfCells = (availableWidth + minimumInteritemSpacing) / (idealCellWidth + minimumInteritemSpacing)
        let numberOfCells = idealNumberOfCells.rounded(.down)
        let cellWidth = (availableWidth + minimumInteritemSpacing) / numberOfCells - minimumInteritemSpacing
        itemSize = CGSize(width: cellWidth, height: cellWidth)
    }

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

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

        let offsetY = collectionView.contentOffset.y + collectionView.safeAreaInsets.top
        if let firstHeader = rectAttributes.first(where: { $0.representedElementKind == UICollectionView.elementKindSectionHeader && offsetY < 0}) {
            let origin = CGPoint(x: firstHeader.frame.origin.x, y: firstHeader.frame.minY - offsetY.magnitude)
            let size = CGSize(width: firstHeader.frame.width, height: max(0, headerReferenceSize.height + offsetY.magnitude))
            firstHeader.frame = CGRect(origin: origin, size: size)
        }

        return rectAttributes
    }

}

CollectionViewCell.swift

import UIKit

class CollectionViewCell: UICollectionViewCell {

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .cyan
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

HeaderReusableView.swift

import UIKit

class HeaderReusableView: UICollectionReusableView {

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .magenta
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}


Expected result:

这篇关于UICollectionView 的弹性标题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆