在没有 `invalidateLayout` 跳转的情况下自动调整 UICollectionViewLayout 中的单元格(支持 iOS 9)
Autosizing cells in UICollectionViewLayout (supporting iOS 9) without `invalidateLayout` jump
我一直在阅读 this article about using custom UICollectionViewLayouts 并尝试将这个想法融入我正在从事的项目中。
我们之前使用的 UICollectionViewFlowLayout
子类有一个巨大的丑陋函数来确定单元格的大小,方法是将原型单元格出列并填充它,然后询问它的大小。
不用说,我想找到更好的方法。
我遇到的问题是单元格的初始布局。我们使用的许多单元格包含 1 或 2 个标签,这些标签可能包含大量文本并且需要换行。在链接的文章中有一个游乐场,它显示自动调整大小的多行标签,但它在文本中插入换行符以强制执行此操作...
在 Datasource.swift 文件中...
lazy private var values: [String] = {
return (0...255).map {
_ in
switch arc4random_uniform(3) {
case 0: return "Hello"
case 1: return "Hello\nGoodbye"
default: return "Hello\nGoodbye\nAu revoir"
}
}
}()
但是,如果我将这些更改为自然会中断的不同长度的字符串,则会破坏示例。初始布局在标签中只有一行。但是,在滚动标签时正确显示。
我可以 "fix" 通过在 Autosizing
文件中添加行 layout.invalidateLayout()
来做到这一点。
现在,我在自己的项目中尝试做同样的事情。如果我将 layout.invalidateLayout()
放在 viewDidAppear
函数中,则只能在视图控制器中使用它。这意味着在转换布局不正确然后 "jumps" 到正确布局之后,我们会得到一个笨拙的跳转。但是,我觉得使用 invalidateLayout
的整个方法都被破坏了。
有什么方法可以让它不破坏初始布局?或者 intelligently
使单元格初始布局无效?
我添加了一个 GitHub project 来说明我遇到的问题。由于代码,目前它的布局正确...
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// This invalidation "fixes" the layout but causes a jump in the UI
collectionView?.collectionViewLayout.invalidateLayout()
}
理想情况下,我想删除它并拥有布局 "just work"。
这里的问题是,在初始传递时,单元格返回的首选布局属性不正确 - 它根据看似无限的宽度调整标签大小,因此您最终想要将其放在一行中。
将此行添加到单元格 setUp()
方法 "fixes" 问题:
label.preferredMaxLayoutWidth = self.bounds.width - (contentView.layoutMargins.left + contentView.layoutMargins.right)
有点脏,我相信有更好的方法。
编辑
好的,这是一个更好的方法。这会迫使您的视图进入布局中的原始估计框架,从而使 AL 引擎有机会正确调整单元格的大小。在您的单元格子类中执行此操作:
private var isInitialLayout = true
public override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
if isInitialLayout {
self.contentView.layoutIfNeeded()
isInitialLayout = false
}
return super.preferredLayoutAttributesFitting(layoutAttributes)
}
我一直在阅读 this article about using custom UICollectionViewLayouts 并尝试将这个想法融入我正在从事的项目中。
我们之前使用的 UICollectionViewFlowLayout
子类有一个巨大的丑陋函数来确定单元格的大小,方法是将原型单元格出列并填充它,然后询问它的大小。
不用说,我想找到更好的方法。
我遇到的问题是单元格的初始布局。我们使用的许多单元格包含 1 或 2 个标签,这些标签可能包含大量文本并且需要换行。在链接的文章中有一个游乐场,它显示自动调整大小的多行标签,但它在文本中插入换行符以强制执行此操作...
在 Datasource.swift 文件中...
lazy private var values: [String] = {
return (0...255).map {
_ in
switch arc4random_uniform(3) {
case 0: return "Hello"
case 1: return "Hello\nGoodbye"
default: return "Hello\nGoodbye\nAu revoir"
}
}
}()
但是,如果我将这些更改为自然会中断的不同长度的字符串,则会破坏示例。初始布局在标签中只有一行。但是,在滚动标签时正确显示。
我可以 "fix" 通过在 Autosizing
文件中添加行 layout.invalidateLayout()
来做到这一点。
现在,我在自己的项目中尝试做同样的事情。如果我将 layout.invalidateLayout()
放在 viewDidAppear
函数中,则只能在视图控制器中使用它。这意味着在转换布局不正确然后 "jumps" 到正确布局之后,我们会得到一个笨拙的跳转。但是,我觉得使用 invalidateLayout
的整个方法都被破坏了。
有什么方法可以让它不破坏初始布局?或者 intelligently
使单元格初始布局无效?
我添加了一个 GitHub project 来说明我遇到的问题。由于代码,目前它的布局正确...
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// This invalidation "fixes" the layout but causes a jump in the UI
collectionView?.collectionViewLayout.invalidateLayout()
}
理想情况下,我想删除它并拥有布局 "just work"。
这里的问题是,在初始传递时,单元格返回的首选布局属性不正确 - 它根据看似无限的宽度调整标签大小,因此您最终想要将其放在一行中。
将此行添加到单元格 setUp()
方法 "fixes" 问题:
label.preferredMaxLayoutWidth = self.bounds.width - (contentView.layoutMargins.left + contentView.layoutMargins.right)
有点脏,我相信有更好的方法。
编辑
好的,这是一个更好的方法。这会迫使您的视图进入布局中的原始估计框架,从而使 AL 引擎有机会正确调整单元格的大小。在您的单元格子类中执行此操作:
private var isInitialLayout = true
public override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
if isInitialLayout {
self.contentView.layoutIfNeeded()
isInitialLayout = false
}
return super.preferredLayoutAttributesFitting(layoutAttributes)
}