Cómo configurar UICollectionViewCell Width y Height mediante programación


103

Estoy tratando de implementar un CollectionView. Cuando utilizo Autolayout, mis celdas no cambiarán el tamaño, sino su alineación.

Ahora prefiero cambiar sus tamaños a, por ejemplo,

//var size = CGSize(width: self.view.frame.width/10, height: self.view.frame.width/10)

Intenté ponerme en mi CellForItemAtIndexPath

collectionCell.size = size

aunque no funcionó.

¿Hay una manera de lograr esto?

editar :

Parece que las respuestas solo cambiarán el ancho y la altura de mi CollectionView. ¿Es posible que haya conflicto en las restricciones? ¿Alguna idea sobre eso?

Respuestas:


267

Utilice este método para establecer un ancho de altura de celda personalizado.

Asegúrese de agregar estos protocolos

UICollectionViewDelegate

UICollectionViewDataSource

UICollectionViewDelegateFlowLayout

Si está utilizando rápida 5 o Xcode 11 y más tarde Es necesario que ajuste Estimate Sizea noneusar guión gráfico con el fin de hacer que funcione correctamente. Si no lo configura, el siguiente código no funcionará como se esperaba.

ingrese la descripción de la imagen aquí

Swift 4 o posterior

extension YourViewController: UICollectionViewDelegate {
    //Write Delegate Code Here
}

extension YourViewController: UICollectionViewDataSource {
    //Write DataSource Code Here
}

extension YourViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: screenWidth, height: screenWidth)
    }
}

C objetivo

@interface YourViewController : UIViewController<UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout>

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return CGSizeMake(CGRectGetWidth(collectionView.frame), (CGRectGetHeight(collectionView.frame)));
}

1
@RamyAlZuhouri Edité mi respuesta, por favor verifique y avíseme si aún necesitamos dejarlo más claro
PinkeshGjr

8
Estoy tan acostumbrado a hacer todo mediante programación que esto me parece tan extraño ahora. En Storyboard, la configuración del Tamaño estimado en Ninguno y la adición de UICollectionViewDelegateFlowLayout es lo que hace el truco
Lance Samaria

5
Establecer Tamaño estimado en ninguno funcionó para mí. Gracias @PinkeshGjr
Mohan

5
Me alegro de haber encontrado esto, perdí 2 horas persiguiéndolo solo para aprender el mágico 'Tamaño estimado a ninguno'
Klajd Deda

3
establecer Tamaño estimado en ninguno salvó mis días.
tounaobun

73

Asegúrese de agregar el protocolo UICollectionViewDelegateFlowLayouten su classdeclaración

class MyCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout
{
    //MARK: - UICollectionViewDelegateFlowLayout

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
    {
       return CGSize(width: 100.0, height: 100.0)
    }
}

No olvide utilizar el diseño de flujo y no el personalizado. Entonces el tuyo es el único que funcionó para mí. ¡Gracias!
Sean

¿Cómo podemos dar una altura dinámica aquí dando una altura estática como 100.0
Sangavi

65

Si uno está usando un guión gráfico y anulando UICollectionViewDelegateFlowLayout , en swift 5 y Xcode 11 también establezca Estimate size en None ingrese la descripción de la imagen aquí


2
Eso fue útil. ¡¡Gracias!!
Naval Hasan

Eso fue definitivamente lo que estaba a punto de volverme loco. A menos que lo establezca en none, no podrá ver el tamaño correcto que ha dado en el método sizeForItem.
Yusuf Kamil AK

2
Establecer el tamaño estimado en ninguno solucionó todo
MMK

2
Thnx configurando el tamaño estimado en ninguno solucionado por problema
Salman500

2
Buen arreglo. Aquí está mi diagnóstico de este problema de acuerdo con la documentación de Apple : UICollectionViewFlowLayoutparece predeterminado estimatedItemSizea UICollectionViewFlowLayout.automaticSizecuando se utiliza IB a pesar de que la documentación dice que debería defecto CGSizeZero. Como afirma Apple, automaticSize"habilita las celdas de tamaño propio para la vista de su colección". Es por eso que otros cambios de tamaño en IB no hacen nada.
Andrew Kirna

20

Finalmente obtuve la respuesta. Debería extender UICollectionViewDelegateFlowLayout
Esto debería funcionar con las respuestas anteriores.


¿Podrías escribir un ejemplo?
Mamdouh El Nakeeb

En realidad, hice una extensión con los protocolos de fuente de datos y delegateFlowLayout y no funcionó. Lo que funcionó fue separar la parte FlowLayout en una extensión propia. No sé por qué, pero funcionó en Swift 3.
Skywalker

15

rápido 4.1

Tiene 2 formas de cambiar el tamaño de CollectionView.
Primera forma -> agregue este protocolo UICollectionViewDelegateFlowLayout
para En mi caso, quiero dividir la celda en 3 partes en una línea. Hice este código a continuación

extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource ,UICollectionViewDelegateFlowLayout{
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
    {
            // In this function is the code you must implement to your code project if you want to change size of Collection view
            let width  = (view.frame.width-20)/3
            return CGSize(width: width, height: width)
    }

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return collectionData.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath)
        if let label = cell.viewWithTag(100) as? UILabel {
            label.text = collectionData[indexPath.row]
        }
        return cell
    }
}

Segunda forma -> no tiene que agregar UICollectionViewDelegateFlowLayout pero tiene que escribir un código en la función viewDidload en su lugar como el código siguiente

class ViewController: UIViewController {
@IBOutlet weak var collectionView1: UICollectionView!
        var collectionData = ["1.", "2.", "3.", "4.", "5.", "6.", "7.", "8.", "9.", "10.", "11.", "12."]

    override func viewDidLoad() {
        super.viewDidLoad()
        let width = (view.frame.width-20)/3
        let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
        layout.itemSize = CGSize(width: width, height: width) 
    }
}


extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource {


    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return collectionData.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath)
        if let label = cell.viewWithTag(100) as? UILabel {
            label.text = collectionData[indexPath.row]
        }

        return cell
    }
}

Independientemente de lo que escriba un código como primera o segunda forma, obtendrá el mismo resultado que el anterior. Yo lo escribi. Funcionó para mi

ingrese la descripción de la imagen aquí


2
Directamente de raywenderlich.com 😂
Andrew Kirna

12

Relación de tamaño según el tamaño del iPhone:

Esto es lo que puede hacer para tener diferentes anchos y altos para las celdas con respecto al tamaño del iPhone:

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    let width = (self.view.frame.size.width - 12 * 3) / 3 //some width
    let height = width * 1.5 //ratio
    return CGSize(width: width, height: height)
}

Y tal vez también debería deshabilitar sus restricciones de Autodiseño en la celda para que esta respuesta funcione.


Probé esto y todas las soluciones anteriores, pero el contenido de una celda no se actualiza automáticamente, ¿cómo puedo solucionar esto?
Biasi Wiga

7

La vista de colección tiene un objeto de diseño . En su caso, probablemente sea un diseño de flujo ( UICollectionViewFlowLayout ). Establezca la itemSizepropiedad del diseño de flujo .


Probé esto. me da el mismo problema que antes. de alguna manera mi CollectionView cambia su tamaño en lugar de mis celdas.
JVS

2
Bueno, todo depende de cuándo lo hagas. No ha mostrado su código real, entonces, ¿quién sabe lo que está haciendo? Te aseguro que funciona.
Matt

7

en Swift3 y Swift4 puede cambiar el tamaño de celda agregando UICollectionViewDelegateFlowLayout e implementando es así:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: 100, height: 100)
    }

o si crea UICollectionView mediante programación, puede hacerlo así:

    let layout = UICollectionViewFlowLayout()
                    layout.scrollDirection = .horizontal //this is for direction
                    layout.minimumInteritemSpacing = 0 // this is for spacing between cells
                    layout.itemSize = CGSize(width: view.frame.width, height: view.frame.height) //this is for cell size
let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)

4

swift4 swift 4 ios colección ver colección ver ejemplo xcode último código muestra de trabajo

Agregue esto en la sección Delegado de la parte superior

UICollectionViewDelegateFlowLayout

y usa esta función

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let width = (self.view.frame.size.width - 20) / 3 //some width
    let height = width * 1.5 //ratio
    return CGSize(width: width, height: height)
}

///// código completo de muestra

crear en la vista de colección y la celda de vista de colección en el guión gráfico hace referencia a la colección como
@IBOutlet débil var cvContent: UICollectionView!

pegar esto en el controlador de vista

 import UIKit

class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

    var arrVeg = [String]()
    var arrFruits = [String]()
    var arrCurrent = [String]()
    
    @IBOutlet weak var cvContent: UICollectionView!
    
  
    
    override func viewDidLoad() {
        super.viewDidLoad()
        arrVeg = ["Carrot","Potato", "Tomato","Carrot","Potato", "Tomato","Carrot","Potato", "Tomato","Carrot","Potato", "Tomato"]
        
        arrVeg = ["Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange","Mango","Papaya","Orange"]
        
        
        arrCurrent = arrVeg
    }
    //MARK: - CollectionView
    


    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let width = (self.view.frame.size.width - 20) / 3 //some width
        let height = width * 1.5 //ratio
        return CGSize(width: width, height: height)
    }
    
    func numberOfSections(in collectionView: UICollectionView) -> Int {

        return 1
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        
        
        return arrCurrent.count
    }
    
    
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ContentCollectionViewCell
        cell.backgroundColor =  UIColor.green
        return cell
    }
}

2

Prueba el método siguiente

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    return CGSize(width: 100.0, height: 100.0)
}

2

Swift 5 mediante programación

lazy var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal

        //Provide Width and Height According to your need
        let cellWidth = UIScreen.main.bounds.width / 10
        let cellHeight = UIScreen.main.bounds.height / 10
        layout.itemSize = CGSize(width: cellWidth, height: cellHeight)

        //You can also provide estimated Height and Width
        layout.estimatedItemSize = CGSize(width: cellWidth, height: cellHeight)

        //For Setting the Spacing between cells
        layout.minimumInteritemSpacing = 0
        layout.minimumLineSpacing = 0

        return UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
    }()

1
**Swift 5**
To make this work you have to do the following.

Add these protocols

 - UICollectionViewDelegate


 - UICollectionViewDataSource


 - UICollectionViewDelegateFlowLayout

Your code will then look like this

extension YourViewController: UICollectionViewDelegate {
    //Write Delegate Code Here
}

extension YourViewController: UICollectionViewDataSource {
    //Write DataSource Code Here
}

extension YourViewController: UICollectionViewDelegateFlowLayout {
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: screenWidth, height: screenWidth)
    }
}

Now the final and crucial step to see this take effect is to go to your viedDidLoad function inside your Viewcontroller.

    override func viewDidLoad() {
        super.viewDidLoad()
        collection.dataSource = self // Add this
        collection.delegate = self // Add this

        // Do any additional setup after loading the view.
    }

Without telling your view which class the delegate is it won't work.

1

2020, de forma absolutamente sencilla:

class YourCollection: UIViewController,
     UICollectionViewDelegate,
     UICollectionViewDataSource {

Debe agregar "UICollectionViewDelegateFlowLayout" o no hay autocompletar:

class YourCollection: UIViewController,
     UICollectionViewDelegate,
     UICollectionViewDataSource,
     UICollectionViewDelegateFlowLayout {

Escriba "sizeForItemAt ...". ¡Listo!

class YourCollection: UIViewController,
     UICollectionViewDelegate,
     UICollectionViewDataSource,
     UICollectionViewDelegateFlowLayout {

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

     return CGSize(width: 37, height: 63)
}

Eso es.

Por ejemplo, si desea "cada celda ocupa toda la vista de la colección":

     guard let b = view.superview?.bounds else { .. }
     return CGSize(width: b.width, height: b.height)

0

Otra forma es establecer el valor directamente en el diseño del flujo.

    let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
    layout.itemSize = CGSize(width: size, height: size)

0

Por lo tanto, debe configurar desde el guión gráfico para el atributo de collectionView en el tamaño estimado de la sección de celda en ninguno, y en su ViewController necesita tener un método delegado para implementar este método: optional func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize


0

Intente utilizar el método UICollectionViewDelegateFlowLayout. En Xcode 11 o posterior, debe configurar Estimate Size en none desde el guión gráfico.

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: 
UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let padding: CGFloat =  170
    let collectionViewSize = advertCollectionView.frame.size.width - padding
    return CGSize(width: collectionViewSize/2, height: collectionViewSize/2)
}

0

Una forma sencilla:

Si solo necesita un tamaño fijo simple :

class SizedCollectionView: UIICollectionView {
    override func common() {
        super.common()
        let l = UICollectionViewFlowLayout()
        l.itemSize = CGSize(width: 42, height: 42)
        collectionViewLayout = l
    }
}

Eso es todo al respecto.

En el guión gráfico, simplemente cambie la clase de UICollectionView a SizedCollectionView.

Pero !!!

Observe que la clase base es "UI 'I' CollectionView". 'I' para inicializador.

No es tan fácil agregar un inicializador a una vista de colección. Este es un enfoque común:

Vista de colección ... con inicializador:

import UIKit

class UIICollectionView: UICollectionView {
    private var commoned: Bool = false
    
    override func didMoveToWindow() {
        super.didMoveToWindow()
        if window != nil && !commoned {
            commoned = true
            common()
        }
    }
    
    internal func common() {
    }
}

En la mayoría de los proyectos, necesita "una vista de colección con un inicializador". Así que probablemente de todos modos tendrá UIICollectionView(tenga en cuenta la I adicional para Initializer) en su proyecto.


0

Swift 5, Programmatic UICollectionView configuración Ancho y alto de celda

// MARK: MyViewController

final class MyViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
    private lazy var collectionViewLayout: UICollectionViewFlowLayout = {
        let layout = UICollectionViewFlowLayout()
        let spacing: CGFloat = 1
        let numOfColumns: CGFloat = 3
        let itemSize: CGFloat = (UIScreen.main.bounds.width - (numOfColumns - spacing) - 2) / 3
        layout.itemSize = CGSize(width: itemSize, height: itemSize)
        layout.minimumInteritemSpacing = spacing
        layout.minimumLineSpacing = spacing
        layout.sectionInset = UIEdgeInsets(top: spacing, left: spacing, bottom: spacing, right: spacing)
        return layout
    }()
    
    private lazy var collectionView: UICollectionView = {
        let collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: collectionViewLayout)
        collectionView.backgroundColor = .white
        collectionView.dataSource = self
        collectionView.delegate = self
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        return collectionView
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureCollectionView()
    }
    
    private func configureCollectionView() {
        view.addSubview(collectionView)
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
            collectionView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            collectionView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)
        ])
        collectionView.register(PhotoCell.self, forCellWithReuseIdentifier: "PhotoCell")
    }
    
    // MARK: UICollectionViewDataSource

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 20
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoCell", for: indexPath) as! PhotoCell
        cell.backgroundColor = .red
        return cell
    }
    
}

// MARK: PhotoCell

final class PhotoCell: UICollectionViewCell {
    
    lazy var imageView: UIImageView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFill
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.layer.masksToBounds = true
        return imageView
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupViews()
    }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init?(coder:) not implemented")
    }
    
    func setupViews() {
        addSubview(imageView)
        NSLayoutConstraint.activate([
            topAnchor.constraint(equalTo: topAnchor),
            bottomAnchor.constraint(equalTo: bottomAnchor),
            leadingAnchor.constraint(equalTo: leadingAnchor),
            trailingAnchor.constraint(equalTo: trailingAnchor)
        ])
    }
    
}

-4

Esta es mi versión, encuentre la proporción adecuada para obtener el tamaño de celda según sus requisitos.

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
return CGSizeMake(CGRectGetWidth(collectionView.frame)/4, CGRectGetHeight(collectionView.frame)/4); 
} 
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.