Swift - 使用 UICollectionViewFlowLayout 的 Facebook 样式图像网格
Posted
技术标签:
【中文标题】Swift - 使用 UICollectionViewFlowLayout 的 Facebook 样式图像网格【英文标题】:Swift - Facebook style image grid using UICollectionViewFlowLayout 【发布时间】:2021-12-29 18:37:18 【问题描述】:我正在尝试在 Swift 中实现 Facebook 样式的图像布局。我找到了this SO question 并遵循了其中一个答案建议的方法。结果,我几乎实现了我想要的。
但是,正如您在上图中看到的那样,存在溢出:三个较小的单元格似乎占据了超过屏幕宽度的空间。但是,如果我将每个单元格的宽度减去0.1
,问题似乎已经解决了。
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
let item = indexPath.item
let width = collectionView.bounds.size.width
let padding:CGFloat = 5
if item < 2
let itemWidth:CGFloat = (width - padding) / 2.0
return CGSize(width: itemWidth, height: itemWidth)
else
let itemWidth:CGFloat = (width - 2 * padding) / 3.0
// let itemWidth:CGFloat = (width - 2 * padding) / 3.0 - 0.1 // This WORKS!!
return CGSize(width: itemWidth, height: itemWidth)
我猜溢出是由itemWidth
的值四舍五入引起的。但我觉得在处理这类问题时,减去一个随机的硬编码值并不是最佳做法。
谁能为此提出更好的方法?
以下是可重现的完整代码。
class ContentSizeCollectionView: UICollectionView
override var contentSize: CGSize
didSet
invalidateIntrinsicContentSize()
override var intrinsicContentSize: CGSize
layoutIfNeeded()
return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
class GridVC: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate, UICollectionViewDataSource
lazy var collectionView: UICollectionView =
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 5
layout.minimumInteritemSpacing = 5
let collectionView = ContentSizeCollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "ident")
return collectionView
()
override func viewDidLoad()
view.addSubview(collectionView)
NSLayoutConstraint.activate([
collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
let item = indexPath.item
let width = collectionView.bounds.size.width
let padding:CGFloat = 5
if item < 2
let itemWidth:CGFloat = (width - padding) / 2.0
return CGSize(width: itemWidth, height: itemWidth)
else
let itemWidth:CGFloat = (width - 2 * padding) / 3.0
return CGSize(width: itemWidth, height: itemWidth)
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return 5
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ident", for: indexPath)
cell.backgroundColor = .red
return cell
更新
floor((width - 2 * padding) / 3.0)
确实防止了溢出,但它在最后留下了一个小间隙。
【问题讨论】:
尝试打印部分插图的集合视图的内容插图。它可能不是 0 @ahezeprint(collectionView.contentInset)
打印零边缘插图。
【参考方案1】:
改变这一行:
let itemWidth:CGFloat = (width - 2 * padding) / 3.0
到这里:
let itemWidth:CGFloat = floor((width - 2 * padding) / 3.0)
您可能也希望使用 2 项行来做到这一点。
编辑
最初的问题是浮点数是近似值。
因此,例如,在 iPhone 8 上,视图宽度为 375
点。单元格宽度计算:
let padding: CGFloat = 5
let itemWidth:CGFloat = (width - 2 * padding) / 3.0
itemWidth
最终成为 121.666... (repeating)
,UIKit 将其解释为 121.66666666666667
。
所以,121.66666666666667 * 3.0 + 10
等于(大致)375.00000000000011
并且...大于375.0
。
所以,collection view 说“不能放在一行上。”
使用floor()
解决了这个问题,除了...... 它遇到了一个非常奇怪的错误!
如果你把numberOfItemsInSection
从5
改为8
,你会得到两行3,右边会有没有间隙 .
我们可以通过使 side 单元格比 center 单元格稍微窄一些来解决这个问题,如下所示:
// we want all three to be the same heights
// 1/3 of (width - 2 * padding)
let itemHeight: CGFloat = (width - 2 * padding) / 3.0
// left and right cells will be 1/3 of (width - 2 * padding)
// rounded down to a whole number
let sideW: CGFloat = floor(itemHeight)
// center cell needs to be
// full-width minus 2 * padding
// minus
// side-width * 2
let centerW: CGFloat = (width - 2 * padding) - sideW * 2
// is it left (0), center (1) or right (2)
let n = (item - 2) % 3
// use the proper width value
let itemWidth: CGFloat = n == 1 ? centerW : sideW
return CGSize(width: itemWidth, height: itemHeight)
或者,似乎的工作是使宽度略小于浮点数的 1/3。您使用了-0.1
,但它也适用于:
let itemWidth:CGFloat = ((width - 2 * padding) / 3.0) - 0.0000001
无论如何,这有望解释“2 个单元而不是 3 个”问题的原因,以及避免它的两个选项。
【讨论】:
请查看更新后的帖子。不幸的是,它没有多大帮助。 @shinhong - 你是在第一行做 2 次,在所有其他行做 3 次吗? @shinhong - 请参阅我的答案的编辑。 感谢您的详尽回答!所以总结一下你的答案,通常floor()
应该解决这种问题(即单元格会自动分布以填充行),但在这种特定情况下,它会在最后导致一个奇怪的间隙?我理解正确吗?
@shinhong - 是的...使用我的答案中的尺寸,floor(121.66666666666667)
等于121
...所以121 * 3 + 10 == 373
将适合375
.只是集合视图中的一个非常非常奇怪的错误。以上是关于Swift - 使用 UICollectionViewFlowLayout 的 Facebook 样式图像网格的主要内容,如果未能解决你的问题,请参考以下文章
ios 8.1:类型“ViewController”不符合协议“UICollectionViewDataSource”
无法从 UIViewController 导航到 UICollectionViewController
使用 UICollectionViewFlowLayout 时在 UICollectionViewCell 的边界之外绘制