¿Cómo establecer el espaciado entre celdas y UICollectionView - UICollectionViewFlowLayout ratio de tamaño?

137

Estoy tratando de añadir UICollectionViewa ViewController, y tengo que tener 3 células 'por fila' sin espacio en blanco entre las células (que debe ser similar una rejilla). El ancho de la celda debe ser un tercio del tamaño de la pantalla, así que pensé que el layout.itemancho debería ser el mismo. Pero luego me sale esto:

layout.itemSize width = screenSize.width / 3

Si reduzco ese tamaño (en 7 u 8 píxeles, por ejemplo), es mejor, pero la tercera celda en la fila no es completamente visible, y todavía tengo ese espacio en blanco (arriba y abajo, e izquierda y derecha).

ingrese la descripción de la imagen aquí

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
fuente
Aquí está tu solución. Gracias.
janu kansagra
Aquí está el código de trabajo para Swift 4 stackoverflow.com/a/54703798/10150796
Nikunj Kumbhani

Respuestas:

299

Agrega estas 2 líneas

layout.minimumInteritemSpacing = 0
layout.minimumLineSpacing = 0

Así que tienes:

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

Eso eliminará todos los espacios y le dará un diseño de cuadrícula:

ingrese la descripción de la imagen aquí

Si desea que la primera columna tenga un ancho igual al ancho de la pantalla, agregue la siguiente función:

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

}

El diseño de la cuadrícula ahora se verá (también he agregado un fondo azul a la primera celda): ingrese la descripción de la imagen aquí

Peter Todd
fuente
Muchas gracias, esperaba algo mucho más complicado y pasé mucho tiempo en ello. Gracias una vez mas.
Marko
Y solo una cosa más, ¿hay alguna posibilidad de cambiar layout.itemSize solo para la primera celda? (Sé que necesito cambiar cell.frame.size cuando indexPath.row es 0, pero no sé cómo cambiar el tamaño del diseño solo para un elemento). El ancho del primer elemento debe ser igual al ancho de pantalla .
Marko
1
Hola, tengo el mismo problema, pero quiero saber el título de la función similar en el Objetivo C.
Nada Gamal
1
Ok, ahora funciona bien, muchas gracias :) Función del objetivo C: - (CGSize) collectionView: (UICollectionView ) collectionView layout: (UICollectionViewLayout ) collectionViewLayout sizeForItemAtIndexPath: (NSIndexPath *) indexPath {return CGSizeMake (screenWidth / 3, screenWidth / 3 ); }
Nada Gamal
44
gracias por la gran respuesta @greentor. envió una recompensa. No te olvides de collectionView! .CollectionViewLayout = diseño , lectores
Fattie
47

Para Swift 3 y XCode 8 , esto funcionó. Siga los pasos a continuación para lograr esto: -

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

Coloque este código en la función viewDidLoad ().

Nirmit Dagly
fuente
32

En ciertas situaciones, la configuración de UICollectionViewFlowLayoutin viewDidLoado ViewWillAppearpuede no afectar el collectionView.

Establecer el UICollectionViewFlowLayoutin viewDidAppearpuede causar ver los cambios de los tamaños de las celdas en tiempo de ejecución.

Otra solución, en 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
fuente
¡Gran respuesta! Definitivamente esta es la forma de Apple de hacerlo. Ahora, no sabes cómo actualizar cuando el dispositivo cambia de orientación, ¿verdad?
jlstr
17

Si está buscando Swift 3, siga los pasos para lograrlo:

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

}

Esto se verifica en Xcode 8.0 con Swift 3.

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

Swift 4

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

Para Swift 3 y XCode 8, esto funcionó. Siga los pasos a continuación para lograr esto: -

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
fuente
3
Cómo tener en cuenta el hecho si tiene varias secciones
Nikhil Pandey
es solo para 1 columna (sección)
usuario924
0

Para Swift 3+ y Xcode 9+ Intente usar esto

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
fuente
0

Swift 5: para espacios distribuidos uniformemente entre celdas con ancho de celda dinámico para aprovechar al máximo el espacio del contenedor, puede usar el fragmento de código a continuación proporcionando un valor mínimo de Ancho de celda .

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
fuente