如何设置单元格间距和 UICollectionView - UICollectionViewFlowLayout 的大小比例?

163

我正在尝试将 UICollectionView 添加到 ViewController 中,并且需要每行显示3个单元格,且单元格之间没有空白(应该看起来像网格)。单元格宽度应为屏幕大小的三分之一,因此我认为 layout.item 的宽度应该相同。但然后我得到了这个:

layout.itemSize width = screenSize.width/3

如果我减小该大小(例如通过78像素),则会好些,但是第三列的单元格不完全可见,并且我仍然有空白空间(顶部和底部,以及左侧和右侧)。

enter image description here

class ViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource {
    var collectionView: UICollectionView?
    var screenSize: CGRect!
    var screenWidth: CGFloat!
    var screenHeight: CGFloat!

    override func viewDidLoad() {
        super.viewDidLoad()

        screenSize = UIScreen.mainScreen().bounds
        screenWidth = screenSize.width
        screenHeight = screenSize.height

        // Do any additional setup after loading the view, typically from a nib
        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        layout.sectionInset = UIEdgeInsets(top: 20, left: 0, bottom: 10, right: 0)
        layout.itemSize = CGSize(width: screenWidth / 3, height: screenWidth / 3)
        collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
        collectionView!.dataSource = self
        collectionView!.delegate = self
        collectionView!.registerClass(CollectionViewCell.self, forCellWithReuseIdentifier: "CollectionViewCell")
        collectionView!.backgroundColor = UIColor.greenColor()
        self.view.addSubview(collectionView!)
    }

    func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
        return 1
    }

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 20
    }

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCellWithReuseIdentifier("CollectionViewCell", forIndexPath: indexPath) as CollectionViewCell
        cell.backgroundColor = UIColor.whiteColor()
        cell.layer.borderColor = UIColor.blackColor().CGColor
        cell.layer.borderWidth = 0.5
        cell.frame.size.width = screenWidth / 3
        cell.frame.size.height = screenWidth / 3

        cell.textLabel?.text = "\(indexPath.section):\(indexPath.row)"
        return cell
    }
}

这是您的解决方案:在这里。 谢谢。 - janu kansagra
1
这里是Swift 4的工作代码:https://stackoverflow.com/a/54703798/10150796 - Nikunj Kumbhani
12个回答

361

添加这两行代码

layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0

所以你有:

   // Do any additional setup after loading the view, typically from a nib.
        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        layout.sectionInset = UIEdgeInsets(top: 20, left: 0, bottom: 10, right: 0)
        layout.itemSize = CGSize(width: screenWidth/3, height: screenWidth/3)
        layout.minimumInteritemSpacing = 0
        layout.minimumLineSpacing = 0
        collectionView!.collectionViewLayout = layout

这将删除所有空格并给您一个网格布局:

enter image description here

如果您希望第一列的宽度等于屏幕宽度,则添加以下函数:

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    if indexPath.row == 0
    {
        return CGSize(width: screenWidth, height: screenWidth/3)
    }
    return CGSize(width: screenWidth/3, height: screenWidth/3);

}

网格布局现在将看起来像这样(我还在第一个单元格中添加了蓝色背景):enter image description here


1
嗨,我有同样的问题,但我想知道在Objective C中类似的函数标题是什么。 - Nada Gamal
1
好的,现在它运行良好了,非常感谢 :) Objective C 函数: -(CGSize)collectionView:(UICollectionView )collectionView layout:(UICollectionViewLayout)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {return CGSizeMake(screenWidth/3, screenWidth/3);} - Nada Gamal
4
谢谢 @greentor 的精彩回答。我已经发了一份赏金。不要忘记 collectionView!.collectionViewLayout = layout,读者们。 - Fattie
@amjad 请查看 https://github.com/petetodd/BGSEmbeddedCVSwift 的示例。在Storyboard的Size Inspector中,您可以更改一些集合视图布局设置,但您可能需要在代码中进行一些计算并设置一些布局值。 - Peter Todd
@amjad,你需要调整集合视图单元格的大小(换行符返回CGSize(宽度:screenWidth / 3,高度:screenWidth / 3))。 - Peter Todd
显示剩余8条评论

54
在某些情况下,在viewDidLoadViewWillAppear中设置UICollectionViewFlowLayout可能不会影响collectionView。
viewDidAppear中设置UICollectionViewFlowLayout可能会导致运行时看到单元格大小的更改。
另一种解决方案,在Swift 3中:
extension YourViewController : UICollectionViewDelegateFlowLayout{

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 20, left: 0, bottom: 10, right: 0)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let collectionViewWidth = collectionView.bounds.width
        return CGSize(width: collectionViewWidth/3, height: collectionViewWidth/3)
    }

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
        return 0
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 20
    }
}

很好的答案!这绝对是苹果的做法。现在,你不会知道如何在设备改变方向时刷新吧? - jlstr

53

对于Swift 3和XCode 8,这个方法有效。按照以下步骤来实现:

{
    let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
    let width = UIScreen.main.bounds.width
    layout.sectionInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
    layout.itemSize = CGSize(width: width / 2, height: width / 2)
    layout.minimumInteritemSpacing = 0
    layout.minimumLineSpacing = 0
    collectionView!.collectionViewLayout = layout
}

将此代码放入viewDidLoad()函数中。


20

如果您正在寻找Swift 3,请按照以下步骤进行:

func viewDidLoad() {

    //Define Layout here
    let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()

    //Get device width
    let width = UIScreen.main.bounds.width

    //set section inset as per your requirement.
    layout.sectionInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)

    //set cell item size here 
    layout.itemSize = CGSize(width: width / 2, height: width / 2)

    //set Minimum spacing between 2 items
    layout.minimumInteritemSpacing = 0

    //set minimum vertical line spacing here between two lines in collectionview
    layout.minimumLineSpacing = 0

    //apply defined layout to collectionview
    collectionView!.collectionViewLayout = layout

}

此代码已在使用Swift 3的Xcode 8.0上验证。


10
let layout = myCollectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.minimumLineSpacing = 8

9

Swift 4, Swift 5,最简单的方法!

无需创建 UICollectionViewFlowLayout() 实例,只需在您的类上实现 UICollectionViewDelegateFlowLayout

extension MyCollectionView: UICollectionViewDelegateFlowLayout {

    // Distance Between Item Cells
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
        return 20
    }
    
    // Cell Margin
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
    }

}

4

Swift 4

let collectionViewLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
collectionViewLayout?.sectionInset = UIEdgeInsetsMake(0, 20, 0, 40) 
collectionViewLayout?.invalidateLayout()

为什么在设置布局后要使其无效? - Luchi Parejo Alcazar

3

Swift 5 : 如果要在动态单元格宽度的情况下均匀分配单元格之间的空间,以充分利用容器空间,您可以使用以下代码片段,提供minimumCellWidth值。

private func collectionViewLayout() -> UICollectionViewLayout {
    
    let layout = UICollectionViewFlowLayout()
    layout.sectionHeadersPinToVisibleBounds = true
    // Important: if direction is horizontal use minimumItemSpacing instead. 
    layout.scrollDirection = .vertical
    
    let itemHeight: CGFloat = 240
    let minCellWidth :CGFloat = 130.0
    let minItemSpacing: CGFloat = 10
    let containerWidth: CGFloat = self.view.bounds.width
    let maxCellCountPerRow: CGFloat =  floor((containerWidth - minItemSpacing) / (minCellWidth+minItemSpacing ))
    
    let itemWidth: CGFloat = floor( ((containerWidth - (2 * minItemSpacing) - (maxCellCountPerRow-1) * minItemSpacing) / maxCellCountPerRow  ) )
    // Calculate the remaining space after substracting calculating cellWidth (Divide by 2 because of left and right insets)
    let inset = max(minItemSpacing, floor( (containerWidth - (maxCellCountPerRow*itemWidth) - (maxCellCountPerRow-1)*minItemSpacing) / 2 ) )

    
    layout.itemSize = CGSize(width: itemWidth, height: itemHeight)
    layout.minimumInteritemSpacing = min(minItemSpacing,inset)
    layout.minimumLineSpacing = minItemSpacing
    layout.sectionInset = UIEdgeInsets(top: minItemSpacing, left: inset, bottom: minItemSpacing, right: inset)

    
    return layout
}

2

在XCode中,我们有一个选项,不需要编写代码,只需选择CollectionView,进入导航器并更改“最小间距”

进入图片描述


2
对于Swift 3+和Xcode 9+,请尝试使用以下代码:
```最初的回答```
extension ViewController: UICollectionViewDelegateFlowLayout {

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    let collectionWidth = collectionView.bounds.width
    return CGSize(width: collectionWidth/3, height: collectionWidth/3)

}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 0
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
    return 0

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接