首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >UICollectionViewCompositionalLayout最小项目大小

UICollectionViewCompositionalLayout最小项目大小
EN

Stack Overflow用户
提问于 2021-04-18 12:04:28
回答 1查看 392关注 0票数 1

我希望我的UICollectionView的最小尺寸是150x150。我希望组合布局确定有多少150x150单元格可以安全地放入环境中,然后在必要时从150x150增加大小,以填补空白,但保持与150x150单元格大小计算的相同列数。

以下是我的尝试,它没有将纵向和横向的最小单元格大小都保持在150x150。

代码语言:javascript
复制
let compositionalLayout = UICollectionViewCompositionalLayout(sectionProvider: { (sectionIndex, environment) -> NSCollectionLayoutSection? in
let fraction: CGFloat = 1 / (floor((environment.container.effectiveContentSize.width / 150)))
let inset: CGFloat = 10

let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(fraction), heightDimension: .fractionalHeight(1))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
item.contentInsets = NSDirectionalEdgeInsets(top: inset, leading: inset, bottom: inset, trailing: inset)

let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .fractionalWidth(fraction))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])

let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: inset, leading: inset, bottom: inset, trailing: inset)

    return section
})

collectionView.collectionViewLayout = compositionalLayout
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-04-19 06:51:35

我认为解决这类问题的方法是首先解决你能想到的最退化、最简单的情况,然后添加任何所需的改进。

因此,让我们忽略所有关于间距的东西,并问问自己:我们如何在一边创建一个由150个或更多正方形单元组成的简单网格?这些单元格将接触集合视图的侧面,并将在所有侧面彼此接触;在这种退化的情况下,没有任何类型的间距。

有一种方法可以做到这一点。我们将使用一个节提供程序函数,以便我们知道集合视图的大小:

代码语言:javascript
复制
let compositionalLayout = UICollectionViewCompositionalLayout(sectionProvider: { 
    (sectionIndex, environment) -> NSCollectionLayoutSection? in

这些方块中有多少可以放在集合视图的宽度中?这是整数除法中的一个简单问题:

代码语言:javascript
复制
    let w = environment.container.effectiveContentSize.width
    let max = Int(w) / 150

现在,我们将每行的项目数指定为一个数字(max),因此项目宽度并不重要;我们将使用一个虚拟值。项目高度将是组的高度,当我们到达它时,我们将跨越这座桥:

代码语言:javascript
复制
    let itemSize = NSCollectionLayoutSize(
        widthDimension: .absolute(10), // this is a dummy value
        heightDimension: .fractionalHeight(1))
    let item = NSCollectionLayoutItem(layoutSize: itemSize)

好了,让我们过桥吧。组的高度需要与项的宽度相同,才能使单元格成为正方形。项目宽度将是集合视图宽度除以每个组的项目数量;这是我们已经计算为max的数字

代码语言:javascript
复制
    let groupSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1),
        heightDimension: .absolute(w/CGFloat(max))) // square
    let group = NSCollectionLayoutGroup
        .horizontal(layoutSize: groupSize, subitem: item, count: max)

然后我们就完成了:

代码语言:javascript
复制
    let section = NSCollectionLayoutSection(group: group)
    return section

试一试。如果你的单元格有一个边框,你可以看到单元格的边缘在哪里,这会很有帮助。你会看到我们现在有大于150的正方形单元,无论是纵向还是横向,当然纵向和横向的单元尺寸是不同的。

好的,在解决了退化情况下的问题之后,让我们考虑一个更复杂的情况。让我们假设所有元素之间都有10个点的空间:外部的边距为10,单元格之间的垂直和水平距离为10个点。我们所要做的就是添加三行:

代码语言:javascript
复制
    group.interItemSpacing = .fixed(10)
    let section = NSCollectionLayoutSection(group: group)
    section.interGroupSpacing = 10
    section.contentInsets = .init(top: 0, leading: 10, bottom: 0, trailing: 10)

这实际上看起来很好,但是我们的数学不再正确工作。我们可以看到这一点,因为单元格不再是平方的。

因此,让我们将插入和间隔插入到我们的数学中。我们的单元格区域的最大宽度现在是160,而不是150,因为单元格的每边将被剥夺5个点。集合视图的宽度必须减少10个点,因为它将在一条边上减少10个点,也就是20个点,但其中10个点被我们刚才的计算放回了原来的位置。所以,把它带到任何地方,我们都会得到这样的结果:

代码语言:javascript
复制
let compositionalLayout = UICollectionViewCompositionalLayout(sectionProvider: {
    (sectionIndex, environment) -> NSCollectionLayoutSection? in
    
    // how many 150-or-larger squares can fit in our width?
    let w = environment.container.effectiveContentSize.width - 10
    let max = Int(w) / (150 + 10)
    
    let itemSize = NSCollectionLayoutSize(
        widthDimension: .absolute(10), // this is a dummy value
        heightDimension: .fractionalHeight(1))
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    
    let groupSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1),
        heightDimension: .absolute(w/CGFloat(max) - 10)) // square
    let group = NSCollectionLayoutGroup
        .horizontal(layoutSize: groupSize, subitem: item, count: max)
    
    group.interItemSpacing = .fixed(10)
    
    let section = NSCollectionLayoutSection(group: group)
    
    section.interGroupSpacing = 10
    section.contentInsets = .init(top: 0, leading: 10, bottom: 0, trailing: 10)
    
    return section
})

这看起来很不错!这是一个没有魔术数字的更一般的版本。我们首先提取所有的幻数作为属性,再加上我已经通过更改一些数字(边距)进行了推广:

代码语言:javascript
复制
var square = 150
var padding : CGFloat = 10
var margin : CGFloat = 16

我们的布局现在看起来像这样:

代码语言:javascript
复制
let compositionalLayout = UICollectionViewCompositionalLayout(sectionProvider: {
    [self] (sectionIndex, environment) -> NSCollectionLayoutSection? in
                
    let w = environment.container.effectiveContentSize.width - CGFloat(margin)*2 + padding
    let max = Int(w) / (square + Int(padding))
    
    let itemSize = NSCollectionLayoutSize(
        widthDimension: .absolute(10), // this is a dummy value
        heightDimension: .fractionalHeight(1))
    let item = NSCollectionLayoutItem(layoutSize: itemSize)
    
    let groupSize = NSCollectionLayoutSize(
        widthDimension: .fractionalWidth(1),
        heightDimension: .absolute(w/CGFloat(max) - padding)) // square
    let group = NSCollectionLayoutGroup
        .horizontal(layoutSize: groupSize, subitem: item, count: max)
    
    group.interItemSpacing = .fixed(padding)
    
    let section = NSCollectionLayoutSection(group: group)
    
    section.interGroupSpacing = padding
    section.contentInsets = .init(top: 0, leading: margin, bottom: 0, trailing: margin)
    
    return section
})

这看起来确实是我们想要的!为了确保这一点,我为它写了一个测试用例:

代码语言:javascript
复制
let vc = ViewController(collectionViewLayout: UICollectionViewFlowLayout())
vc.loadViewIfNeeded()
vc.collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")
for _ in 1..<3 {
    vc.margin = CGFloat(Int.random(in:7..<20))
    vc.padding = CGFloat(Int.random(in:7..<20))
    vc.square = Int.random(in:140..<170)
    var foundMin = false
    for w : CGFloat in stride(from: 280, to: 500, by: 1) {
        for h : CGFloat in stride(from: 280, to: 800, by: 10) {

            vc.view.frame = CGRect(x: 0, y:0, width: w, height: h)
            vc.view.layoutIfNeeded()
            let cell = vc.collectionView.visibleCells.first!
            let sz = cell.bounds.size
            XCTAssertEqual(sz.width, sz.height, accuracy: 0.1)
            XCTAssertGreaterThanOrEqual(sz.width, CGFloat(vc.square))
            if sz.width == CGFloat(vc.square) { foundMin = true }
        }
    }
    XCTAssertTrue(foundMin)
}

这里的想法是要表明我们的单元格始终是正方形的,有时它们是所需的最小值,但永远不会小于-这正是您在问题的术语中强加的条件。我们使用依赖注入来随机化初始条件。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67145078

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档