简体   繁体   English

UICollectionView动态header尺寸

[英]UICollectionView dynamic header size

I have a collectionView with a header designed in a.xib file.我有一个在 a.xib 文件中设计的带有 header 的 collectionView。 It has a simple label and it's text supports dynamicType.它有一个简单的 label,它的文本支持动态类型。

How can I set the height of that header to be dynamic based on that label and the auto layout constraints in Storyboard?如何根据 label 和 Storyboard 中的自动布局约束将 header 的高度设置为动态?

So far, I've got this:到目前为止,我有这个:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    let kind = UICollectionView.elementKindSectionHeader
    let indexPath = IndexPath(row: 0, section: section)
    if let headerView = collectionView.supplementaryView(forElementKind: kind, at: indexPath) as? SectionHeaderView {
        headerView.layoutIfNeeded()
        headerView.setNeedsLayout()
        let size = headerView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
        return size
    }
    return CGSize(width: 0, height: 0)
}

But it does not show any header.但它没有显示任何 header。

SectionHeaderView.xib looks like this: SectionHeaderView.xib 如下所示: 头文件.xib

CollectionView looks like this: you see 3 sections, but you don't see a header. CollectionView 看起来像这样:您看到 3 个部分,但没有看到 header。 集合视图

What can I do to let AutoLayout determine the correct hight of the header?我该怎么做才能让 AutoLayout 确定 header 的正确高度?

Use a custom flow layout to perfectly manage the height of a header in a collection view with the Dynamic Type feature.使用自定义流布局在具有Dynamic Type功能的集合视图中完美管理 header 的高度。

A header element is seen as a supplementary element for a collection view and the referenceSizeForHeaderInSection 'method' is only used for initialization: it's not called with the Dynamic Type feature. header 元素被视为集合视图的补充元素referenceSizeForHeaderInSection '方法' 仅用于初始化:它不使用Dynamic Type功能调用。

The solution hereafter is based on the layoutAttributesForElements method of the custom layout that will be able to adapt the header height thanks to the UIFontMetrics scaledValue .此后的解决方案基于自定义布局的layoutAttributesForElements方法,该方法能够适应 header 高度,这要归功于UIFontMetrics scaledValue

All that is fired by the invalidateLayout method called in the traitCollectionDidChange triggered when the user changes the font size.当用户更改字体大小时,所有这些都由在traitCollectionDidChange中调用的invalidateLayout方法触发。

STEP 1 ⟹ create a simple custom header class as follows for instance: STEP 1 ⟹ 创建一个简单的自定义 header class 例如如下:

class MyHeaderClass: UICollectionReusableView {

    override init(frame: CGRect) { super.init(frame: frame) }

    required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
}

STEP 2 ⟹ create a new empty.xib adding a reusable view and name it the exact same name as the class it refers to: don't forget to change its class name in the Identity Inspector .第 2步 ⟹ 创建一个新的 empty.xib,添加一个可重用视图,并将其命名为与它所指的 class 完全相同的名称:不要忘记在Identity Inspector中更改其 class 名称。

STEP 3 ⟹ register the.xib file in the controller: STEP 3 ⟹ 在controller中注册.xib文件:

collectionView.register(UINib(nibName: collectionViewHeaderFooterReuseIdentifier bundle: nil),
                        forSupplementaryViewOfKind: UICollectionElementKindSectionHeader,
                        withReuseIdentifier:collectionViewHeaderFooterReuseIdentifier)

STEP 4 ⟹ support this new cell in your data source (a header is a supplementary element for a collection view) : STEP 4 ⟹ 在您的数据源中支持这个新单元格(header 是集合视图的补充元素)

func collectionView(_ collectionView: UICollectionView,
                    viewForSupplementaryElementOfKind kind: String,
                    at indexPath: IndexPath) -> UICollectionReusableView {

    if (kind == UICollectionView.elementKindSectionHeader) {
        let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: kind,
                                                                         withReuseIdentifier: collectionViewHeaderReuseIdentifier,
                                                                         for: indexPath) as! MyHeader
        headerView.myLabel.text = "Your Header Title"
        return headerView
    } else {
        return UICollectionReusableView(frame: CGRect.null) }
}

... and in your delegate (this intializes the header size and makes it appear) : ...并在您的代表中(这会初始化 header 大小并使其出现)

func collectionView(_ collectionView: UICollectionView,
                    layout collectionViewLayout: UICollectionViewLayout,
                    referenceSizeForHeaderInSection section: Int) -> CGSize {

    return CGSize(width: collectionView.frame.width, height: headerHeight)
}

.. after adding a global var headerHeight: CGFloat = 90.0 for initialization. .. 添加全局var headerHeight: CGFloat = 90.0进行初始化后。

STEP 5 ⟹ create the custom flow layout to adapt the header height to the new font size:第 5步 ⟹ 创建自定义流布局以使 header 高度适应新的字体大小:

class FlowLayout: UICollectionViewFlowLayout {

    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {

        let layoutAttributes = super.layoutAttributesForElements(in: rect)

        layoutAttributes?.forEach({ (attribute) in
            if (attribute.representedElementKind == UICollectionView.elementKindSectionHeader) {

                headerHeight = UIFontMetrics.default.scaledValue(for: 22.0)
                attribute.frame.size.height = headerHeight
            }
        })

        return layoutAttributes
    }
}

Don't forget to update the storyboard in Interface Builder :不要忘记在Interface Builder中更新 storyboard : 在此处输入图像描述 STEP 6 ⟹ inform the controller to trigger a layout update when the user changes the font size: STEP 6 ⟹ 通知 controller 在用户更改字体大小时触发布局更新:

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    if (previousTraitCollection?.preferredContentSizeCategory != traitCollection.preferredContentSizeCategory) {
        collectionView?.collectionViewLayout.invalidateLayout()
    }
}

Following this rationale, the correct height of the headers is automatically set up according to the headers font size ⟹ I suggest to use the Xcode 11 new feature to test the Dynamic Type very quickly.根据这个原理,标题的正确高度是根据标题字体大小自动设置的⟹我建议使用Xcode 11 新功能非常快速地测试Dynamic Type

Assuming you've got the proper constraints setup for your header, and you return a dynamically deduced height in your referenceSizeForHeaderInSection :假设您已经为 header 设置了正确的约束,并且您在referenceSizeForHeaderInSection中返回了一个动态推导的高度:

Observe UIContentSizeCategory.didChangeNotification in your class:观察UIContentSizeCategory.didChangeNotification中的 UIContentSizeCategory.didChangeNotification:

NotificationCenter.default.addObserver(self, selector: #selector(fontSizeChanged), name: UIContentSizeCategory.didChangeNotification, object: nil)

Use your fontSizeChanged function for updation:使用您的fontSizeChanged function 进行更新:

@objc private func fontSizeChanged(_ sender: Any) {
    //If you're dealing with minimal data, you can simply 'reloadData()'
    //If not, I'm sure there are other efficient ways to make this work. I'm just exposing the provision
    self.collectionView.reloadData()
}

Oh, and make sure you:哦,请确保您:

deinit {
    NotificationCenter.default.removeObserver(self)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM