簡體   English   中英

UICollectionView動態header尺寸

[英]UICollectionView dynamic header size

我有一個在 a.xib 文件中設計的帶有 header 的 collectionView。 它有一個簡單的 label,它的文本支持動態類型。

如何根據 label 和 Storyboard 中的自動布局約束將 header 的高度設置為動態?

到目前為止,我有這個:

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)
}

但它沒有顯示任何 header。

SectionHeaderView.xib 如下所示: 頭文件.xib

CollectionView 看起來像這樣:您看到 3 個部分,但沒有看到 header。 集合視圖

我該怎么做才能讓 AutoLayout 確定 header 的正確高度?

使用自定義流布局在具有Dynamic Type功能的集合視圖中完美管理 header 的高度。

header 元素被視為集合視圖的補充元素referenceSizeForHeaderInSection '方法' 僅用於初始化:它不使用Dynamic Type功能調用。

此后的解決方案基於自定義布局的layoutAttributesForElements方法,該方法能夠適應 header 高度,這要歸功於UIFontMetrics scaledValue

當用戶更改字體大小時,所有這些都由在traitCollectionDidChange中調用的invalidateLayout方法觸發。

STEP 1 ⟹ 創建一個簡單的自定義 header class 例如如下:

class MyHeaderClass: UICollectionReusableView {

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

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

第 2步 ⟹ 創建一個新的 empty.xib,添加一個可重用視圖,並將其命名為與它所指的 class 完全相同的名稱:不要忘記在Identity Inspector中更改其 class 名稱。

STEP 3 ⟹ 在controller中注冊.xib文件:

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

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) }
}

...並在您的代表中(這會初始化 header 大小並使其出現)

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

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

.. 添加全局var headerHeight: CGFloat = 90.0進行初始化后。

第 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
    }
}

不要忘記在Interface Builder中更新 storyboard : 在此處輸入圖像描述 STEP 6 ⟹ 通知 controller 在用戶更改字體大小時觸發布局更新:

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

根據這個原理,標題的正確高度是根據標題字體大小自動設置的⟹我建議使用Xcode 11 新功能非常快速地測試Dynamic Type

假設您已經為 header 設置了正確的約束,並且您在referenceSizeForHeaderInSection中返回了一個動態推導的高度:

觀察UIContentSizeCategory.didChangeNotification中的 UIContentSizeCategory.didChangeNotification:

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

使用您的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()
}

哦,請確保您:

deinit {
    NotificationCenter.default.removeObserver(self)
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM