- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我的目标是在 UICollectionView
中拥有动态大小的单元格。此外,当UICollectionView
的可滚动contentWidth
小于集合的bounds
时,我希望项目在集合内居中。
到目前为止的方法:
我正在尝试以尽可能最干净的方式做到这一点。我有自动调整大小的 UICollectionViewCells,其中自动布局可以根据内容的约束确定单元格的大小。为此,我的单元格重写了 preferredLayoutAttributesFitting
函数,返回其内容的适当宽度。
我的自定义 UICollectionViewFlowLayout
子类使用 UICollectionViewCell
提供的宽度信息来正确调整单元格的大小。
在我的自定义 UICollectionViewFlowLayout
上重写的唯一方法是:
layoutAttributesForItem(at...)
layoutAttributesForElementsIn(矩形...)
shouldInvalidateLayout(forBoundsChange...)
UICollectionViewCells
的动态大小调整工作完美。
问题:
使用这种方法,我事先不知道每个单元格的大小(每个单元格可以不同)。我想实现 UICollectionView
的 delegate
方法来提供一个插图,使我的内容在 Collection View 中居中。为此,我想我应该实现并覆盖
collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
问题在于 inset 方法是在布局过程的早期调用的,我不知道布局期间将确定的 contentSize
。
以下是单个完整布局过程的各种布局方法的堆栈,按它们运行的顺序排列:
prepare started
prepare finished
layoutAttributesForElementsInRect started
layoutAttributesForElementsInRect finished
layoutAttributesForElementsInRect started
layoutAttributesForElementsInRect finished
layoutAttributesForElementsInRect started
layoutAttributesForElementsInRect finished
prepare started
insetForSectionAt 0 called <--- The datasource has knowledge of the presence of cells but the cells themselves are not yet dequeued
prepare finished
layoutAttributesForElementsInRect started
layoutAttributesForItemAt [0, 0] started
layoutAttributesForItemAt [0, 0] finished
layoutAttributesForItemAt [0, 1] started
layoutAttributesForItemAt [0, 1] finished
layoutAttributesForItemAt [0, 2] started
layoutAttributesForItemAt [0, 2] finished
layoutAttributesForElementsInRect finished
layoutAttributesForElementsInRect started
layoutAttributesForItemAt [0, 0] started
layoutAttributesForItemAt [0, 0] finished
layoutAttributesForItemAt [0, 1] started
layoutAttributesForItemAt [0, 1] finished
layoutAttributesForItemAt [0, 2] started
layoutAttributesForItemAt [0, 2] finished
layoutAttributesForElementsInRect finished
prepare started
prepare finished
layoutAttributesForElementsInRect started
<--- At this point Cells DO return valid sizes through Autolayout
layoutAttributesForItemAt [0, 0] started
layoutAttributesForItemAt [0, 0] finished
layoutAttributesForItemAt [0, 1] started
layoutAttributesForItemAt [0, 1] finished
layoutAttributesForItemAt [0, 2] started
layoutAttributesForItemAt [0, 2] finished
layoutAttributesForElementsInRect finished
对于单个布局过程,会多次调用各种布局方法,但只有在最后才能将来自不同单元格的自动布局的大小信息传递到布局。
我注意到,在单元格正确出列并允许运行其自动布局约束之前,sectionInset
委托(delegate)函数被提前调用。因此,在调用时不可能告知 inset 正确的值。
使用这种方法,是否可以在布局传递后强制调用重新计算 sectionInsetAt
或者我应该完全更改技术?
我尝试过的事情:
我尝试从 insetForSectionAt
委托(delegate)函数中查询 collectionViewLayout
的 collectionViewContentSize
。不幸的是,这不能在布局过程中使用,因为布局当前正在进行中。它会导致崩溃并出现错误:
[CollectionView] An attempt to update layout information was detected while already in the process of computing the layout (i.e. reentrant call). This will result in unexpected behaviour or a crash. This may happen if a layout pass is triggered while calling out to a delegate. UICollectionViewFlowLayout instance is (<App.ViewportToolbarFlowLayout: 0x7f8a5261add0>)
我尝试使用collectionView
的contentSize
方法来获取insetForSectionAt
委托(delegate)方法执行期间的大小,但它确实在布局过程中运行时不提供正确的数据。
最佳答案
在这里发布答案。我发现this answer作者:Alex Koshy,这对我帮助很大。
基本上,该工作必须在 layoutAttributesForElementsIn(rect...)
中执行,因为它是唯一可用的完整布局图片的地方。我已经调整了 Alex 的代码来处理水平和垂直的 UICollectionViews(这可能不是每个人都想要的,但正是我需要的)。我使用布局的 scrollDirection
属性来确定元素是水平居中(当 scrollDirection
为 .horizontal
时)还是垂直居中(当 >scrollDirection
是 .vertical
)。
这是代码:
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let collectionView = collectionView else { return nil }
guard let superLayoutAttributes = super.layoutAttributesForElements(in: rect) else { return nil }
let computedAttributes = superLayoutAttributes.compactMap { layoutAttribute -> UICollectionViewLayoutAttributes? in
return layoutAttribute.representedElementCategory == .cell ? layoutAttributesForItem(at: layoutAttribute.indexPath) : layoutAttribute
}
guard let attributes = NSArray(array: computedAttributes, copyItems: true) as? [UICollectionViewLayoutAttributes] else { return nil }
let interItemSpacing = minimumInteritemSpacing
switch scrollDirection {
case .horizontal:
let leftPadding: CGFloat = self.sectionInset.left
var leftMargin: CGFloat = leftPadding // Modified to determine origin.x for each item
var maxY: CGFloat = -1.0 // Modified to determine origin.y for each item
var rowSizes: [[CGFloat]] = [] // Tracks the starting and ending x-values for the first and last item in the row
var currentRow: Int = 0 // Tracks the current row
attributes.forEach { layoutAttribute in
// Each layoutAttribute represents its own item
if layoutAttribute.frame.origin.y >= maxY {
// This layoutAttribute represents the left-most item in the row
leftMargin = leftPadding
currentRow += rowSizes.isEmpty ? 0 : 1
// Register its origin.x in rowSizes for use later
rowSizes.append([leftMargin, 0])
}
layoutAttribute.frame.origin.x = leftMargin
leftMargin += layoutAttribute.frame.width + interItemSpacing
maxY = max(layoutAttribute.frame.maxY, maxY)
// Add right-most x value for last item in the row
rowSizes[currentRow][1] = leftMargin - interItemSpacing
}
// At this point, all cells are left aligned
// Reset tracking values and add extra left padding to center align entire row
leftMargin = leftPadding
maxY = -1.0
currentRow = 0
attributes.forEach { (layoutAttribute) in
if layoutAttribute.frame.origin.y >= maxY {
// This layoutAttribute represents the left-most item in the row
leftMargin = leftPadding
// Need to bump it up by an appended margin
let rowWidth = rowSizes[currentRow][1] - rowSizes[currentRow][0] // last.x - first.x
let appendedMargin = (collectionView.frame.width - leftPadding - rowWidth - leftPadding) / 2
leftMargin += appendedMargin
currentRow += 1
}
layoutAttribute.frame.origin.x = leftMargin
leftMargin += layoutAttribute.frame.width + interItemSpacing
maxY = max(layoutAttribute.frame.maxY, maxY)
}
case .vertical:
let topPadding: CGFloat = self.sectionInset.top
var topMargin: CGFloat = topPadding
var maxX: CGFloat = -1.0 // Modified to determine origine.x for each item
var colSizes: [[CGFloat]] = [] // Tracks the starting and ending y-values for the first and last item in the column
var currentCol: Int = 0
attributes.forEach { (layoutAttribute) in
if layoutAttribute.frame.origin.x >= maxX {
topMargin = topPadding
currentCol += colSizes.isEmpty ? 0 : 1
colSizes.append([topMargin, 0])
}
layoutAttribute.frame.origin.y = topMargin
topMargin += layoutAttribute.frame.height + interItemSpacing
maxX = max(layoutAttribute.frame.maxX, maxX)
colSizes[currentCol][1] = topMargin - interItemSpacing
}
topMargin = topPadding
maxX = -1.0
currentCol = 0
attributes.forEach { (layoutAttribute) in
if layoutAttribute.frame.origin.x >= maxX {
topMargin = topPadding
let colWidth = colSizes[currentCol][1] - colSizes[currentCol][0]
let appendedMargin = (collectionView.frame.height - topPadding - colWidth - topPadding) / 2
topMargin += appendedMargin
currentCol += 1
}
layoutAttribute.frame.origin.y = topMargin
topMargin += layoutAttribute.frame.height + interItemSpacing
maxX = max(layoutAttribute.frame.maxX, maxX)
}
}
return attributes
}
关于ios - 如何使用动态大小的单元格在 UICollectionViewFlowLayout 子类上正确设置部分插入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55078289/
这个问题在这里已经有了答案: C sizeof a passed array [duplicate] (7 个回答) 8年前关闭。 在一个函数中,我声明了一个数组: int char_count_ar
简而言之,文件系统如何与 block 设备通信? 最佳答案 我对 block 大小不太了解。我认为 ext4(Linux)的文件系统的 block 大小是 4KB,考虑到现代处理器的页面大小(4KB)
我知道 tinyint(1) 和 tinyint(2) 具有相同的存储空间范围。 唯一的区别是显示宽度不同。这是否意味着 tinyint(1) 将存储所有类型的整数但只正确显示 0 到 9 的范围?而
今晚我已经研究了以下代码几个小时,但我只是摸不着头脑。 当使用函数从标准输入填充数组时,我不断收到“大小 8 的无效写入”和“大小 8 的无效读取”。 如有任何帮助,我们将不胜感激...我知道 Sta
我有一个 valgrind 错误,我不知道如何摆脱它们: ==5685== Invalid read of size 8 ==5685== at 0x4008A1: main (in /home
我对 Hadoop 的概念有点困惑。 Hadoop block 大小、拆分大小和 block 大小 之间有什么区别? 提前致谢。 最佳答案 block 大小和 block 大小相同。 拆分大小 可能与
我想不出一个好的标题,所以希望可以。 我正在做的是创建一个离线 HTML5 webapp。 “出于某些原因”我不希望将某些文件放在缓存 list 中,而是希望将内容放在 localStorage 中。
无法将 xamarin apk 大小减少到 80 MB 以下,已执行以下操作: 启用混淆器 配置:发布 平台:事件(任何 CPU)。 启用 Multi-Dex:true 启用开发人员检测(调试和分析)
我正在开发一个程序,需要将大量 csv 文件(数千个)加载到数组中。 csv 文件的尺寸为 45x100,我想创建一个尺寸为 nx45x100 的 3-d 数组。目前,我使用 pd.read_csv(
Hello World 示例的 React Native APK 大小约为 20M (in recent versions),因为支持不同的硬件架构(ARMv7、ARMv8、X86 等),而同一应用程
我有一个包含 n 个十进制元素的列表,其中每个元素都是两个字节长。 可以说: x = [9000 , 5000 , 2000 , 400] 这个想法是将每个元素拆分为 MSB 和 LSB 并将其存储在
如何设置 GtKTextView 的大小?我想我不能使用 gtk_widget_set_usize。 最佳答案 您不能直接控制小部件的大小,而是由其容器完成。您可以使用 gtk_widget_set_
这个问题在这里已经有了答案: c++ sizeof() of a class with functions (7 个答案) 关闭 5 年前。 结果是 12。 foobar 函数存储在内存中的什么位置
当我在 ffmpeg(或任何其他程序)中使用这样的命令时: ffmpeg -i input.mp4 image%d.jpg 所有图像的组合文件大小总是比视频本身大。我尝试减少每秒帧数、降低压缩设置、模
我是 clojurescript 的新手。 高级编译后出现“77 KB”的javascript文件是否正常? 我有一个 clojurescript 文件: 我正在使用 leinigen: lein c
我想要一个 QPixmap尺寸为 50 x 50。 我试过 : QPixmap watermark(QSize(50,50)); watermark.load(":/icoMenu/preparati
我正在尝试从一篇研究论文中重新创建一个 cnn,但我对深度学习还是个新手。 我得到了一个大小为 32x32x7 的 3d 补丁。我首先想执行一个大小为 3x3 的卷积,具有 32 个特征和步幅为 2。
我一直在尝试调整 View Controller 内的 View 大小,但到目前为止没有运气。基本上,我的 View 最底部有一个按钮,当方向从纵向更改为横向时,该按钮不再可见,因为它现在太靠下了。
如何使用此功能检查图像的尺寸?我只是想在上传之前检查一下... $("#LINK_UPLOAD_PHOTO").submit(function () { var form = $(this);
我用 C++ 完成了这个,因为你可以通过引用传递参数。我无法弄清楚如何在 JavaScript 中执行此操作。我的代码需要更改什么?我的输出是1 this.sizeOfBst = function()
我是一名优秀的程序员,十分优秀!