Comment définir l'espacement des cellules et le rapport de taille UICollectionView - UICollectionViewFlowLayout?

137

J'essaie d'ajouter UICollectionViewà ViewController, et j'ai besoin d'avoir 3 cellules «par ligne» sans espace vide entre les cellules (cela devrait ressembler à une grille). La largeur de la cellule doit être un tiers de la taille de l'écran, j'ai donc pensé que la layout.itemlargeur devait être la même. Mais alors je comprends ceci:

layout.itemSize width = screenSize.width / 3

Si je réduis cette taille (de 7 ou 8 pixels par exemple), c'est mieux, mais la troisième cellule de la rangée n'est pas complètement visible, et j'ai toujours cet espace vide (haut et bas, gauche et droite).

entrez la description de l'image ici

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
    }
}
Marko
la source
Voici votre solution. Merci.
janu kansagra
Voici le code de travail pour Swift 4 stackoverflow.com/a/54703798/10150796
Nikunj Kumbhani

Réponses:

299

Ajoutez ces 2 lignes

layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0

Donc vous avez:

   // 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

Cela supprimera tous les espaces et vous donnera une disposition en grille:

entrez la description de l'image ici

Si vous souhaitez que la première colonne ait une largeur égale à la largeur de l'écran, ajoutez la fonction suivante:

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);

}

La disposition de la grille ressemblera maintenant à (j'ai également ajouté un fond bleu à la première cellule): entrez la description de l'image ici

Peter Todd
la source
Merci beaucoup, je m'attendais à quelque chose de beaucoup plus compliqué et j'ai passé beaucoup de temps dessus. Merci encore une fois.
Marko
Et juste une chose de plus, est-il possible de changer layout.itemSize pour la première cellule uniquement? (Je sais que je dois changer cell.frame.size lorsque l' indexPath.row est 0, mais je ne sais pas comment changer la taille de la mise en page uniquement pour un élément) La largeur du premier élément doit être égale à screenWidth .
Marko
1
Salut, j'ai le même problème mais je veux connaître le titre de fonction similaire dans Objective C.
Nada Gamal
1
Ok, cela fonctionne bien maintenant, merci beaucoup :) Fonction Objective C: - (CGSize) collectionView: (UICollectionView ) collectionView layout: (UICollectionViewLayout ) collectionViewLayout sizeForItemAtIndexPath: (NSIndexPath *) indexPath {return CGSizeMake (screenWidth / 3, screenWidth / 3, screenWidth / 3 ); }
Nada Gamal
4
merci pour l'excellente réponse @greentor. envoyé une prime. N'oubliez pas la collectionView! .CollectionViewLayout = layout , reader
Fattie
47

Pour Swift 3 et XCode 8 , cela a fonctionné. Suivez les étapes ci-dessous pour y parvenir: -

{
    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
}

Placez ce code dans la fonction viewDidLoad ().

Nirmit Dagly
la source
32

Dans certaines situations, la définition de UICollectionViewFlowLayoutin viewDidLoadou ViewWillAppearpeut ne pas affecter la collectionView.

La définition de UICollectionViewFlowLayoutin viewDidAppearpeut entraîner des modifications de la taille des cellules lors de l'exécution.

Une autre solution, dans 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
    }
}
Maor
la source
Très bonne réponse! C'est définitivement la façon Apple de le faire. Maintenant, vous ne savez pas comment actualiser lorsque l'appareil change d'orientation, n'est-ce pas?
jlstr
17

Si vous recherchez Swift 3, suivez les étapes pour y parvenir:

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

}

Ceci est vérifié sur Xcode 8.0 avec Swift 3.

Dharmendra Kachhadiya
la source
8
let layout = myCollectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.minimumLineSpacing = 8
jamal zare
la source
4

Swift 4

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

Pour Swift 3 et XCode 8, cela a fonctionné. Suivez les étapes ci-dessous pour y parvenir: -

viewDidLoad()
    {
        let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
        var width = UIScreen.main.bounds.width
        layout.sectionInset = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
    width = width - 10
    layout.itemSize = CGSize(width: width / 2, height: width / 2)
        layout.minimumInteritemSpacing = 0
        layout.minimumLineSpacing = 0
        collectionView!.collectionViewLayout = layout
    }
Moin Shirazi
la source
3
Comment prendre en compte le fait si vous avez plusieurs sections
Nikhil Pandey
c'est seulement pour 1 colonne (section)
user924
0

Pour Swift 3+ et Xcode 9+ Essayez d'utiliser ceci

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

}
Aditya
la source
0

Swift 5: pour des espaces uniformément répartis entre les cellules avec une largeur de cellule dynamique afin de tirer le meilleur parti de l'espace du conteneur, vous pouvez utiliser l'extrait de code ci-dessous en fournissant une valeur 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
}
deniz yalcin
la source