tutorial example collection ios objective-c xcode uicollectionview uicollectionviewcell

ios - example - uicollectionview swift 4 tutorial



UICollectionView Establecer el nĂºmero de columnas (15)

Acabo de empezar a aprender sobre UICollectionViews. Me pregunto si alguien sabe cómo especificar el número de columnas en una vista de colección. El valor predeterminado está establecido en 3 (iPhone / retrato). Miré la documentación y no puedo encontrar una respuesta concisa.


Ampliando la respuesta de noob:

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize { let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout let totalSpace = flowLayout.sectionInset.left + flowLayout.sectionInset.right + (flowLayout.minimumInteritemSpacing * CGFloat(numberOfItemsPerRow - 1)) let size = Int((collectionView.bounds.width - totalSpace) / CGFloat(numberOfItemsPerRow)) return CGSize(width: size, height: size) }

Esto permite cualquier espacio entre las celdas. Asume una variable miembro Int llamada numberOfItemsPerRow y también que todas las celdas son cuadradas y del mismo tamaño. Como se señala en la respuesta de jhilgert00, también debemos reaccionar a los cambios de orientación, pero ahora al usar viewWillTransitionToSize , se va a depreciar willRotateToInterfaceOrientation .


Aquí está el código de trabajo para Swift 3, para tener un diseño de dos columnas:

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let nbCol = 2 let flowLayout = collectionViewLayout as! UICollectionViewFlowLayout let totalSpace = flowLayout.sectionInset.left + flowLayout.sectionInset.right + (flowLayout.minimumInteritemSpacing * CGFloat(nbCol - 1)) let size = Int((collectionView.bounds.width - totalSpace) / CGFloat(nbCol)) return CGSize(width: size, height: size) }

Siéntase libre de cambiar "nbCol" a su número deseado de columnas.


Con Swift 4.1 e iOS 11, puede usar una de las siguientes cuatro implementaciones para establecer el número de elementos por fila en su UICollectionView mientras maneja las inserciones y los cambios de tamaño (incluida la rotación).

# 1. UICollectionViewFlowLayout y usando la propiedad itemSize

ColumnFlowLayout.swift:

import UIKit class ColumnFlowLayout: UICollectionViewFlowLayout { let cellsPerRow: Int init(cellsPerRow: Int, minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) { self.cellsPerRow = cellsPerRow super.init() self.minimumInteritemSpacing = minimumInteritemSpacing self.minimumLineSpacing = minimumLineSpacing self.sectionInset = sectionInset } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func prepare() { super.prepare() guard let collectionView = collectionView else { return } let marginsAndInsets = sectionInset.left + sectionInset.right + collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right + minimumInteritemSpacing * CGFloat(cellsPerRow - 1) let itemWidth = ((collectionView.bounds.size.width - marginsAndInsets) / CGFloat(cellsPerRow)).rounded(.down) itemSize = CGSize(width: itemWidth, height: itemWidth) } override func invalidationContext(forBoundsChange newBounds: CGRect) -> UICollectionViewLayoutInvalidationContext { let context = super.invalidationContext(forBoundsChange: newBounds) as! UICollectionViewFlowLayoutInvalidationContext context.invalidateFlowLayoutDelegateMetrics = newBounds.size != collectionView?.bounds.size return context } }

CollectionViewController.swift:

import UIKit class CollectionViewController: UICollectionViewController { let columnLayout = ColumnFlowLayout( cellsPerRow: 5, minimumInteritemSpacing: 10, minimumLineSpacing: 10, sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) ) override func viewDidLoad() { super.viewDidLoad() collectionView?.collectionViewLayout = columnLayout collectionView?.contentInsetAdjustmentBehavior = .always collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell") } override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 59 } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) cell.backgroundColor = UIColor.orange return cell } }

# 2. Usando el método itemSize

import UIKit class CollectionViewController: UICollectionViewController { let margin: CGFloat = 10 let cellsPerRow = 5 override func viewDidLoad() { super.viewDidLoad() guard let collectionView = collectionView, let flowLayout = collectionViewLayout as? UICollectionViewFlowLayout else { return } flowLayout.minimumInteritemSpacing = margin flowLayout.minimumLineSpacing = margin flowLayout.sectionInset = UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin) collectionView.contentInsetAdjustmentBehavior = .always collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell") } override func viewWillLayoutSubviews() { guard let collectionView = collectionView, let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else { return } let marginsAndInsets = flowLayout.sectionInset.left + flowLayout.sectionInset.right + collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right + flowLayout.minimumInteritemSpacing * CGFloat(cellsPerRow - 1) let itemWidth = ((collectionView.bounds.size.width - marginsAndInsets) / CGFloat(cellsPerRow)).rounded(.down) flowLayout.itemSize = CGSize(width: itemWidth, height: itemWidth) } override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 59 } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) cell.backgroundColor = UIColor.orange return cell } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { collectionView?.collectionViewLayout.invalidateLayout() super.viewWillTransition(to: size, with: coordinator) } }

# 3. Usando el UICollectionViewDelegateFlowLayout collectionView(_:layout:sizeForItemAt:)

import UIKit class CollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout { let inset: CGFloat = 10 let minimumLineSpacing: CGFloat = 10 let minimumInteritemSpacing: CGFloat = 10 let cellsPerRow = 5 override func viewDidLoad() { super.viewDidLoad() collectionView?.contentInsetAdjustmentBehavior = .always collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "Cell") } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { return UIEdgeInsets(top: inset, left: inset, bottom: inset, right: inset) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return minimumLineSpacing } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { return minimumInteritemSpacing } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let marginsAndInsets = inset * 2 + collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right + minimumInteritemSpacing * CGFloat(cellsPerRow - 1) let itemWidth = ((collectionView.bounds.size.width - marginsAndInsets) / CGFloat(cellsPerRow)).rounded(.down) return CGSize(width: itemWidth, height: itemWidth) } override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 59 } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) cell.backgroundColor = UIColor.orange return cell } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { collectionView?.collectionViewLayout.invalidateLayout() super.viewWillTransition(to: size, with: coordinator) } }

# 4. Subclase de UICollectionViewFlowLayout y uso de la propiedad de estimatedItemSize UICollectionViewFlowLayout

CollectionViewController.swift:

import UIKit class CollectionViewController: UICollectionViewController { let items = [ "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "Lorem ipsum dolor sit amet, consectetur.", "Lorem ipsum dolor sit amet.", "Lorem ipsum dolor sit amet, consectetur.", "Lorem ipsum dolor sit amet, consectetur adipiscing.", "Lorem ipsum.", "Lorem ipsum dolor sit amet.", "Lorem ipsum dolor sit.", "Lorem ipsum dolor sit amet, consectetur adipiscing.", "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.", "Lorem ipsum dolor sit amet, consectetur." ] let columnLayout = FlowLayout( cellsPerRow: 3, minimumInteritemSpacing: 10, minimumLineSpacing: 10, sectionInset: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) ) override func viewDidLoad() { super.viewDidLoad() collectionView?.collectionViewLayout = columnLayout collectionView?.contentInsetAdjustmentBehavior = .always collectionView?.register(Cell.self, forCellWithReuseIdentifier: "Cell") } override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return items.count } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell cell.label.text = items[indexPath.row] return cell } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { collectionView?.collectionViewLayout.invalidateLayout() super.viewWillTransition(to: size, with: coordinator) } }

FlowLayout.swift:

import UIKit class FlowLayout: UICollectionViewFlowLayout { let cellsPerRow: Int required init(cellsPerRow: Int = 1, minimumInteritemSpacing: CGFloat = 0, minimumLineSpacing: CGFloat = 0, sectionInset: UIEdgeInsets = .zero) { self.cellsPerRow = cellsPerRow super.init() self.minimumInteritemSpacing = minimumInteritemSpacing self.minimumLineSpacing = minimumLineSpacing self.sectionInset = sectionInset estimatedItemSize = UICollectionViewFlowLayoutAutomaticSize } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { guard let layoutAttributes = super.layoutAttributesForItem(at: indexPath) else { return nil } guard let collectionView = collectionView else { return layoutAttributes } let marginsAndInsets = collectionView.safeAreaInsets.left + collectionView.safeAreaInsets.right + sectionInset.left + sectionInset.right + minimumInteritemSpacing * CGFloat(cellsPerRow - 1) layoutAttributes.bounds.size.width = ((collectionView.bounds.width - marginsAndInsets) / CGFloat(cellsPerRow)).rounded(.down) return layoutAttributes } override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { let superLayoutAttributes = super.layoutAttributesForElements(in: rect)!.map { $0.copy() as! UICollectionViewLayoutAttributes } guard scrollDirection == .vertical else { return superLayoutAttributes } let layoutAttributes = superLayoutAttributes.compactMap { layoutAttribute in return layoutAttribute.representedElementCategory == .cell ? layoutAttributesForItem(at: layoutAttribute.indexPath) : layoutAttribute } // (optional) Uncomment to top align cells that are on the same line /* let cellAttributes = layoutAttributes.filter({ $0.representedElementCategory == .cell }) for (_, attributes) in Dictionary(grouping: cellAttributes, by: { ($0.center.y / 10).rounded(.up) * 10 }) { guard let max = attributes.max(by: { $0.size.height < $1.size.height }) else { continue } for attribute in attributes where attribute.size.height != max.size.height { attribute.frame.origin.y = max.frame.origin.y } } */ // (optional) Uncomment to bottom align cells that are on the same line /* let cellAttributes = layoutAttributes.filter({ $0.representedElementCategory == .cell }) for (_, attributes) in Dictionary(grouping: cellAttributes, by: { ($0.center.y / 10).rounded(.up) * 10 }) { guard let max = attributes.max(by: { $0.size.height < $1.size.height }) else { continue } for attribute in attributes where attribute.size.height != max.size.height { attribute.frame.origin.y += max.frame.maxY - attribute.frame.maxY } } */ return layoutAttributes } }

Cell.swift:

import UIKit class Cell: UICollectionViewCell { let label = UILabel() override init(frame: CGRect) { super.init(frame: frame) label.numberOfLines = 0 backgroundColor = .orange addSubview(label) label.translatesAutoresizingMaskIntoConstraints = false label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor).isActive = true label.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor).isActive = true label.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor).isActive = true label.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor).isActive = true } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { layoutIfNeeded() label.preferredMaxLayoutWidth = label.bounds.size.width layoutAttributes.bounds.size.height = systemLayoutSizeFitting(UILayoutFittingCompressedSize).height return layoutAttributes } }


Debido a que UICollectionView es tan flexible, existen varias maneras de cambiar la cantidad de columnas, según el tipo de diseño que utilice.

UICollectionViewFlowLayout (que es probablemente con lo que está trabajando) no especifica un número de columnas directamente (porque depende del tamaño / orientación de la vista). La forma más fácil de cambiarlo sería establecer la propiedad itemSize y / o minimumInteritemSpacing / minimumInteritemSpacing .


Es todo acerca del diseño que quieres dibujar. Puede crear clases personalizadas heredadas de UICollectionViewFlowLayout. Actualmente no hay ningún método directo para establecer columnas. Si desea lograr este tipo de funcionalidad, debe hacerlo manualmente. Debe manejarlo en su clase de diseño de flujo personalizado.

Ahora surgirá la pregunta de cómo lo harás? Si no quieres alterar el marco de la celda, puedes ajustar

collectionView:layout:minimumInteritemSpacingForSectionAtIndex: collectionView:layout:minimumLineSpacingForSectionAtIndex:

Otra forma es proporcionarle sus propias posiciones de celdas. Anulando debajo de dos métodos, que serán llamados durante tu formación de disposición.

- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)path

UICollectionViewLayoutAttributes es una clase que se ocupará de la posición de la celda, el marco, Zindex, etc.


Esta respuesta es para swift 3.0 ->

primero cumplir con el protocolo:

UICollectionViewDelegateFlowLayout

luego agregue estos métodos:

//this method is for the size of items func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let width = collectionView.frame.width/3 let height : CGFloat = 160.0 return CGSize(width: width, height: height) } //these methods are to configure the spacing between items func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { return UIEdgeInsetsMake(0,0,0,0) } 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 0 }


Implementé UICollectionViewDelegateFlowLayout en mi UICollectionViewController y UICollectionViewController el método responsable de determinar el tamaño de la Celda. Luego tomé el ancho de la pantalla y lo dividí con el requisito de mi columna. Por ejemplo, quería tener 3 columnas en cada tamaño de pantalla. Así que aquí está mi código:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { CGRect screenRect = [[UIScreen mainScreen] bounds]; CGFloat screenWidth = screenRect.size.width; float cellWidth = screenWidth / 3.0; //Replace the divisor with the column count requirement. Make sure to have it in float. CGSize size = CGSizeMake(cellWidth, cellWidth); return size; }


Los CollectionView son muy potentes y tienen un precio. Montones y muchas opciones. Como dijo omz:

hay varias maneras en que puede cambiar el número de columnas

Sugeriría implementar el <UICollectionViewDelegateFlowLayout> , que le da acceso a los siguientes métodos en los que puede tener un mayor control sobre el diseño de su UICollectionView , sin la necesidad de subclasificarlo:

  • collectionView:layout:insetForSectionAtIndex:
  • collectionView:layout:minimumInteritemSpacingForSectionAtIndex:
  • collectionView:layout:minimumLineSpacingForSectionAtIndex:
  • collectionView:layout:referenceSizeForFooterInSection:
  • collectionView:layout:referenceSizeForHeaderInSection:
  • collectionView:layout:sizeForItemAtIndexPath:

Además, la implementación del siguiente método obligará a su UICollectionView a actualizar su diseño en un cambio de orientación: (digamos que desea cambiar el tamaño de las celdas para el paisaje y hacerlas estirar)

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration{ [self.myCollectionView.collectionViewLayout invalidateLayout]; }

Además, aquí hay 2 tutoriales realmente buenos en UICollectionViews :

http://www.raywenderlich.com/22324/beginning-uicollectionview-in-ios-6-part-12

http://skeuo.com/uicollectionview-custom-layout-tutorial


Si eres perezoso usando delegado.

extension UICollectionView { func setItemsInRow(items: Int) { if let layout = self.collectionViewLayout as? UICollectionViewFlowLayout { let contentInset = self.contentInset let itemsInRow: CGFloat = CGFloat(items); let innerSpace = layout.minimumInteritemSpacing * (itemsInRow - 1.0) let insetSpace = contentInset.left + contentInset.right + layout.sectionInset.left + layout.sectionInset.right let width = floor((CGRectGetWidth(frame) - insetSpace - innerSpace) / itemsInRow); layout.itemSize = CGSizeMake(width, width) } } }

PD: debe llamarse después de la rotación también


Solo quería anexar la respuesta # 2 de Imanou Petit. Para garantizar que los márgenes sean exactos independientemente del ancho de la pantalla, utilizo un solucionador iterativo con un margen deseado, y el número de columnas como entradas. También agregué una bandera direccional en donde se compararán sus márgenes finales con su objetivo.

El solucionador iterativo se muestra a continuación y devuelve cellWidth y margin.

private func iterativeCellSpacing(targetMargins : CGFloat, cellsPerRow : Int, isMinTarget : Bool) -> (CGFloat, CGFloat) { var w : CGFloat = 0 var m : CGFloat = targetMargins let cols : CGFloat = CGFloat(cellsPerRow) let numMargins : CGFloat = cols + 1.0 let screenWidth : CGFloat = collectionView!.bounds.size.width var delta = CGFloat.greatestFiniteMagnitude while abs(delta) > 0.001 { let totalMarginSpacing = numMargins * m let totalCellSpacing = screenWidth - totalMarginSpacing if (isMinTarget) { w = floor(totalCellSpacing / cols) m = ceil((screenWidth - cols * w) / numMargins) } else { w = ceil(totalCellSpacing / cols) m = floor((screenWidth - cols * w) / numMargins) } delta = screenWidth - w * CGFloat(cellsPerRow) - m * numMargins } return (w, m) }

Lo llamo así:

fileprivate var margin: CGFloat = 20 fileprivate var cellWidth : CGFloat = 80 fileprivate let cellsPerRow = 4 override func viewDidLoad() { super.viewDidLoad() (cellWidth, margin) = iterativeCellSpacing(targetMargins: margin, cellsPerRow: 4, isMinTarget: true) ... }

A continuación, aplico los valores cellWidth y margin al diseño de flujo como tal:

extension MyCollectionController : UICollectionViewDelegateFlowLayout

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

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { collectionView?.collectionViewLayout.invalidateLayout() super.viewWillTransition(to: size, with: coordinator) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { return UIEdgeInsetsMake(margin, margin, margin, margin) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return margin } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { return margin }

}

Espero que esto ayude. Probablemente haya una manera más fácil de garantizar que los márgenes sean exactos, pero este es un método. Además, este código no se ha probado para dispositivos que permiten la rotación de vistas de colección.

Gracias,


la solución perfecta es usar UICollectionViewDelegateFlowLayout, pero puede calcular fácilmente el ancho de la celda y dividirla en el número deseado de columnas que desee

lo difícil es hacer el ancho sin ninguna fracción

(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { CGFloat screenWidth = self.view.frame.size.width; CGFloat marginWidth = (screenWidth - collectionView.frame.size.width); CGFloat cellWith = (collectionView.frame.size.width - marginWidth )/3; cellWith= floorf(cellWith); CGSize retval = CGSizeMake(cellWith,cellWith); return retval;}


Actualizado a Swift 3:

En lugar del diseño de flujo, prefiero usar un diseño personalizado para el número de columna y el número de fila específicos. Porque:

  1. Se puede arrastrar horizontalmente si el número de columna es muy grande.
  2. Es más aceptable lógicamente debido al uso de columna y fila.

Celda normal y Celda de encabezado: (Agregue UILabel como IBOutlet a su xib):

class CollectionViewCell: UICollectionViewCell { @IBOutlet weak var label: UILabel! override func awakeFromNib() { super.awakeFromNib() // Initialization code self.backgroundColor = UIColor.black label.textColor = UIColor.white } } class CollectionViewHeadCell: UICollectionViewCell { @IBOutlet weak var label: UILabel! override func awakeFromNib() { super.awakeFromNib() // Initialization code self.backgroundColor = UIColor.darkGray label.textColor = UIColor.white } }

Diseño personalizado:

let cellHeight: CGFloat = 100 let cellWidth: CGFloat = 100 class CustomCollectionViewLayout: UICollectionViewLayout { private var numberOfColumns: Int! private var numberOfRows: Int! // It is two dimension array of itemAttributes private var itemAttributes = [[UICollectionViewLayoutAttributes]]() // It is one dimension of itemAttributes private var cache = [UICollectionViewLayoutAttributes]() override func prepare() { if self.cache.isEmpty { self.numberOfColumns = self.collectionView?.numberOfItems(inSection: 0) self.numberOfRows = self.collectionView?.numberOfSections // Dynamically change cellWidth if total cell width is smaller than whole bounds /* if (self.collectionView?.bounds.size.width)!/CGFloat(self.numberOfColumns) > cellWidth { self.cellWidth = (self.collectionView?.bounds.size.width)!/CGFloat(self.numberOfColumns) } */ for row in 0..<self.numberOfRows { var row_temp = [UICollectionViewLayoutAttributes]() for column in 0..<self.numberOfColumns { let indexPath = NSIndexPath(item: column, section: row) let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath) attributes.frame = CGRect(x: cellWidth*CGFloat(column), y: cellHeight*CGFloat(row), width: cellWidth, height: cellHeight) row_temp.append(attributes) self.cache.append(attributes) } self.itemAttributes.append(row_temp) } } } override var collectionViewContentSize: CGSize { return CGSize(width: CGFloat(self.numberOfColumns)*cellWidth, height: CGFloat(self.numberOfRows)*cellHeight) } override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { var layoutAttributes = [UICollectionViewLayoutAttributes]() for attributes in cache { if attributes.frame.intersects(rect) { layoutAttributes.append(attributes) } } return layoutAttributes } }

CollectionView:

let CellIdentifier = "CellIdentifier" let HeadCellIdentifier = "HeadCellIdentifier" class CollectionView: UICollectionView, UICollectionViewDelegate, UICollectionViewDataSource { init() { let layout = CustomCollectionViewLayout() super.init(frame: CGRect.zero, collectionViewLayout: layout) self.register(UINib(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: CellIdentifier) self.register(UINib(nibName: "CollectionViewHeadCell", bundle: nil), forCellWithReuseIdentifier: HeadCellIdentifier) self.isDirectionalLockEnabled = true self.dataSource = self self.delegate = self } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func updateCollectionView() { DispatchQueue.main.async { self.reloadData() } } // MARK: CollectionView datasource func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 20 } func numberOfSections(in collectionView: UICollectionView) -> Int { return 20 } override func numberOfItems(inSection section: Int) -> Int { return 20 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let column = (indexPath as NSIndexPath).row let row = (indexPath as NSIndexPath).section if column == 0 { let cell : CollectionViewHeadCell = collectionView.dequeueReusableCell(withReuseIdentifier: HeadCellIdentifier, for: indexPath) as! CollectionViewHeadCell cell.label.text = "/(row)" return cell } else if row == 0 { let cell : CollectionViewHeadCell = collectionView.dequeueReusableCell(withReuseIdentifier: HeadCellIdentifier, for: indexPath) as! CollectionViewHeadCell cell.label.text = "/(column)" return cell } else { let cell : CollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: CellIdentifier, for: indexPath) as! CollectionViewCell cell.label.text = String(format: "%d", arguments: [indexPath.section*indexPath.row]) return cell } } // MARK: CollectionView delegate func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let column = (indexPath as NSIndexPath).row let row = (indexPath as NSIndexPath).section print("/(column) /(row)") } }

Use CollectionView desde ViewController:

class ViewController: UIViewController { let collectionView = CollectionView() override func viewDidLoad() { collectionView.translatesAutoresizingMaskIntoConstraints = false self.view.addSubview(collectionView) self.view.backgroundColor = UIColor.red self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[collectionView]|", options: [], metrics: nil, views: ["collectionView": collectionView])) self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[collectionView]|", options: [], metrics: nil, views: ["collectionView": collectionView])) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) collectionView.updateCollectionView() } }

¡Finalmente puedes tener una elegante CollectionView!


Swift 3.0. Funciona con direcciones de desplazamiento horizontal y vertical y espaciado variable

Especifique el número de columnas

let numberOfColumns: CGFloat = 3

Configure flowLayout para representar el numberOfColumns especificado

if let flowLayout = collectionView?.collectionViewLayout as? UICollectionViewFlowLayout { let horizontalSpacing = flowLayout.scrollDirection == .vertical ? flowLayout.minimumInteritemSpacing : flowLayout.minimumLineSpacing let cellWidth = (collectionView.frame.width - max(0, numberOfColumns - 1)*horizontalSpacing)/numberOfColumns flowLayout.itemSize = CGSize(width: cellWidth, height: cellWidth) }


Hice un diseño de colección.

Para hacer que el separador sea visible, configure el color de fondo de la vista de colección en gris. Una fila por sección

Uso:

let layout = GridCollectionViewLayout() layout.cellHeight = 50 // if not set, cellHeight = Collection.height/numberOfSections layout.cellWidth = 50 // if not set, cellWidth = Collection.width/numberOfItems(inSection) collectionView.collectionViewLayout = layout

Diseño:

import UIKit class GridCollectionViewLayout: UICollectionViewLayout { var cellWidth : CGFloat = 0 var cellHeight : CGFloat = 0 var seperator: CGFloat = 1 private var cache = [UICollectionViewLayoutAttributes]() override func prepare() { guard let collectionView = self.collectionView else { return } self.cache.removeAll() let numberOfSections = collectionView.numberOfSections if cellHeight <= 0 { cellHeight = (collectionView.bounds.height - seperator*CGFloat(numberOfSections-1))/CGFloat(numberOfSections) } for section in 0..<collectionView.numberOfSections { let numberOfItems = collectionView.numberOfItems(inSection: section) let cellWidth2 : CGFloat if cellWidth <= 0 { cellWidth2 = (collectionView.bounds.width - seperator*CGFloat(numberOfItems-1))/CGFloat(numberOfItems) } else { cellWidth2 = cellWidth } for row in 0..<numberOfItems { let indexPath = NSIndexPath(row: row, section: section) let attributes = UICollectionViewLayoutAttributes(forCellWith: indexPath as IndexPath) attributes.frame = CGRect(x: (cellWidth2+seperator)*CGFloat(row), y: (cellHeight+seperator)*CGFloat(section), width: cellWidth2, height: cellHeight) //row_temp.append(attributes) self.cache.append(attributes) } //self.itemAttributes.append(row_temp) } } override var collectionViewContentSize: CGSize { guard let collectionView = collectionView else { return CGSize.zero } if (collectionView.numberOfSections <= 0) { return collectionView.bounds.size } let width:CGFloat if cellWidth <= 0 { width = collectionView.bounds.width } else { width = cellWidth*CGFloat(collectionView.numberOfItems(inSection: 0)) } let numberOfSections = CGFloat(collectionView.numberOfSections) var height:CGFloat = 0 height += numberOfSections * cellHeight height += (numberOfSections - 1) * seperator return CGSize(width: width, height: height) } override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { var layoutAttributes = [UICollectionViewLayoutAttributes]() for attributes in cache { if attributes.frame.intersects(rect) { layoutAttributes.append(attributes) } } return layoutAttributes } override func layoutAttributesForItem(at indexPath: IndexPath) -> UICollectionViewLayoutAttributes? { return cache[indexPath.item] } }


Prueba esto, funciona perfecto para mí,

Siga los pasos a continuación:

1. Defina los valores en el archivo .h.

#define kNoOfColumsForCollection 3 #define kNoOfRowsForCollection 4 #define kcellSpace 5 #define kCollectionViewCellWidth (self.view.frame.size.width - kcellSpace*kNoOfColumsForCollection)/kNoOfColumsForCollection #define kCollectionViewCellHieght (self.view.frame.size.height-40- kcellSpace*kNoOfRowsForCollection)/kNoOfRowsForCollection

O

#define kNoOfColumsForCollection 3 #define kCollectionViewCellWidthHieght (self.view.frame.size.width - 6*kNoOfColumsForCollection)/kNoOfColumsForCollection

2.Agregar código en los métodos de fuente de datos de vista de distribución de la siguiente manera,

#pragma mark Collection View Layout data source methods // collection view with autolayout - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { return 4; } - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { return 1; } - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { return UIEdgeInsetsMake(4, 4, 4, 4); } - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { return CGSizeMake(kCollectionViewCellWidth,kCollectionViewCellHieght); // CGSizeMake (kCollectionViewCellWidthHieght,kCollectionViewCellWidthHieght); }

Espero que esto sea de ayuda para alguien.