[英]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 如下所示:
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.