[英]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。
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.