¿Cómo puedo centrar filas en UICollectionView?

Tengo una UICollectionView con celdas aleatorias. ¿Hay algún método que me permita centrar filas?

Así es como se ve por defecto:

 [ xxxxxx ] [ xxxxxx ] [ xx ] 

Aquí es cómo se ve el layout deseado:

 [ xxxxx ] [ xxxxx ] [ xx ] 

Un poco de background primero: una UICollectionView se combina con un UICollectionViewLayout que determina cómo se colocan las celdas en la vista. Esto significa que una vista de colección es muy flexible (puedes crear casi cualquier layout con ella), pero también significa que modificar los layouts puede ser un poco confuso.

La creación de una class de layout completamente nueva es compleja, por lo tanto, quiere intentar y modificar el layout pnetworkingeterminado ( UICollectionViewFlowLayout ) para get la alignment de su centro. Para hacerlo aún más simple, probablemente desee evitar subclassar el layout de flujo en sí mismo.

Aquí hay un enfoque (puede que no sea el mejor enfoque, pero es el primero en el que puedo pensar): divide tus células en dos secciones, de la siguiente manera:

 [ xxxxx ] <-- Section 1 [ xxxxx ] <-- Section 1 [ xx ] <-- Section 2 

Esto debería ser bastante sencillo, siempre que sepa el ancho de la vista de desplazamiento y el número de celdas que pueden caber en cada fila.

Luego, use collectionView:layout:insetForSectionAtIndex: delegate method para establecer los márgenes de su segunda sección para que parezca que está cinput verticalmente. Una vez que hayas hecho esto, solo debes asegurarte de recomstackr las divisiones / recuadros correspondientes de la sección, de modo que las orientaciones vertical y horizontal puedan ser compatibles.

Aquí hay una pregunta un tanto similar: ¿Cómo alinear las celdas de una UICollectionView? – eso entra en más detalles sobre los methods de inserción, aunque no está tratando de hacer lo mismo que tú.

Tenía que hacer algo como esto, pero necesitaba todas las celdas en una sección. Fue muy simple extender UICollectionViewFlowLayout a las celdas centrales. Hice una cápsula:

https://github.com/keighl/KTCenterFlowLayout

introduzca la descripción de la imagen aquí

En caso de que alguien tenga una CollectionView que tenga 2 columnas, y si el número de elementos es impar, el último elemento debe estar alineado en el centro. Entonces usa esto

DNLastItemCentenetworkingLayout

 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { NSArray *attributes = [super layoutAttributesForElementsInRect:rect]; for (UICollectionViewLayoutAttributes *attribute in attributes) { NSInteger itemCount = [self.collectionView.dataSource collectionView:self.collectionView numberOfItemsInSection:attribute.indexPath.section]; if (itemCount % 2 == 1 && attribute.indexPath.item == itemCount - 1) { CGRect originalFrame = attribute.frame; attribute.frame = CGRectMake(self.collectionView.bounds.size.width/2-originalFrame.size.width/2, originalFrame.origin.y, originalFrame.size.width, originalFrame.size.height); } } return attributes; } 

Esto se puede lograr con un layout personalizado (relativamente) simple, subclasificado de UICollectionViewFlowLayout . Aquí hay un ejemplo en Swift :

 /** * A simple `UICollectionViewFlowLayout` subclass that would make sure the items are center-aligned in the collection view, when scrolling vertically. */ class UICollectionViewFlowCenterLayout: UICollectionViewFlowLayout { override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { guard let suggestedAttributes = super.layoutAttributesForElementsInRect(rect) else { return nil } guard scrollDirection == .Vertical else { return suggestedAttributes } var newAttributes: [UICollectionViewLayoutAttributes] = [] /// We will collect items for each row in this array var currentRowAttributes: [UICollectionViewLayoutAttributes] = [] /// We will use this variable to detect new rows when iterating over items var yOffset:CGFloat = sectionInset.top for attributes in suggestedAttributes { /// If we happen to run into a new row... if attributes.frame.origin.y != yOffset { /* * Update layout of all items in the previous row and add them to the resulting array */ centerSingleRowWithItemsAttributes(&currentRowAttributes, rect: rect) newAttributes += currentRowAttributes /* * Reset the accumulated values for the new row */ currentRowAttributes = [] yOffset = attributes.frame.origin.y } currentRowAttributes += [attributes] } /* * Update the layout of the last row. */ centerSingleRowWithItemsAttributes(&currentRowAttributes, rect: rect) newAttributes += currentRowAttributes return newAttributes } /** Updates the attributes for items, so that they are center-aligned in the given rect. - parameter attributes: Attributes of the items - parameter rect: Bounding rect */ private func centerSingleRowWithItemsAttributes(inout attributes: [UICollectionViewLayoutAttributes], rect: CGRect) { guard let item = attributes.last else { return } let itemsCount = CGFloat(attributes.count) let sideInsets = rect.width - (item.frame.width * itemsCount) - (minimumInteritemSpacing * (itemsCount - 1)) var leftOffset = sideInsets / 2 for attribute in attributes { attribute.frame.origin.x = leftOffset leftOffset += attribute.frame.width + minimumInteritemSpacing } } } 

He subclassado UICollectionViewFlowLayoutUICollectionViewFlowLayout el código que encontré aquí para la vista de colección alineada a la izquierda.

  1. Alinear a la izquierda la vista de colección
  2. Agrupe los attributes para las matrices de línea.
  3. Para cada línea:
    • calcular el espacio en el lado derecho
    • agregar la mitad del espacio para cada atributo de la línea

Se ve así:

 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { NSArray *attributesForElementsInRect = [super layoutAttributesForElementsInRect:rect]; NSMutableArray *newAttributesForElementsInRect = [[NSMutableArray alloc] initWithCapacity:attributesForElementsInRect.count]; CGFloat leftMargin = self.sectionInset.left; NSMutableArray *lines = [NSMutableArray array]; NSMutableArray *currLine = [NSMutableArray array]; for (UICollectionViewLayoutAttributes *attributes in attributesForElementsInRect) { // Handle new line BOOL newLine = attributes.frame.origin.x <= leftMargin; if (newLine) { leftMargin = self.sectionInset.left; //will add outside loop currLine = [NSMutableArray arrayWithObject:attributes]; } else { [currLine addObject:attributes]; } if ([lines indexOfObject:currLine] == NSNotFound) { [lines addObject:currLine]; } // Align to the left CGRect newLeftAlignedFrame = attributes.frame; newLeftAlignedFrame.origin.x = leftMargin; attributes.frame = newLeftAlignedFrame; leftMargin += attributes.frame.size.width + self.minimumInteritemSpacing; [newAttributesForElementsInRect addObject:attributes]; } // Center left aligned lines for (NSArray *line in lines) { UICollectionViewLayoutAttributes *lastAttributes = line.lastObject; CGFloat space = CGRectGetWidth(self.collectionView.frame) - CGRectGetMaxX(lastAttributes.frame); for (UICollectionViewLayoutAttributes *attributes in line) { CGRect newFrame = attributes.frame; newFrame.origin.x = newFrame.origin.x + space / 2; attributes.frame = newFrame; } } return newAttributesForElementsInRect; } 

Espero que ayude a alguien 🙂

Simplemente vincula este layout de flujo. Puede alinear el centro, la izquierda y la derecha también.

  // // CellAllignmentFlowLayout.swift // UICollectionView // // Created by rajeshkumar Lingavel on 8/11/15. // Copyright © 2015 rajeshkumar Lingavel. All rights reserved. // import UIKit enum SZAlignment:Int { case Center, left, Right } class CellAllignmentFlowLayout: UICollectionViewFlowLayout { var alignment:SZAlignment! var padding:CGFloat! override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { // NSArray *allAttributesInRect = [super // layoutAttributesForElementsInRect:rect]; let allAttributesInRect:NSArray = super.layoutAttributesForElementsInRect(rect)! var changedAttributes:NSArray = NSArray() switch(alignment.rawValue){ case 0: changedAttributes = alignCenter(allAttributesInRect) case 1: changedAttributes = alignLeft(allAttributesInRect) case 2: changedAttributes = alignRight(allAttributesInRect) default: assertionFailure("No Direction") } return changedAttributes as? [UICollectionViewLayoutAttributes] } private func alignCenter(allAttributesInRect:NSArray) -> NSArray{ let numberOfSection:Int = (self.collectionView?.numberOfSections())! let networkingefiendArray = NSMutableArray() for i in 0 ..< numberOfSection { let thisSectionObjects = sectionObjects(allAttributesInRect, section: i) let totalLines = numberOfLines(thisSectionObjects, section: i) let lastrowObjects = lastRow(thisSectionObjects, numberOfRows: totalLines, section: i) let lastRowObjectsRow = setMiddleTheLastRow(lastrowObjects) let start = (thisSectionObjects.count - lastrowObjects.count) for j in start..<thisSectionObjects.count{ thisSectionObjects.replaceObjectAtIndex(j, withObject: lastRowObjectsRow.objectAtIndex(j - start)) } networkingefiendArray.addObjectsFromArray(thisSectionObjects as [AnyObject]) } return networkingefiendArray } private func alignLeft(allAttributesInRect:NSArray) -> NSArray{ return allAttributesInRect; } private func alignRight(allAttributesInRect:NSArray) -> NSArray{ return allAttributesInRect; } private func getTotalLenthOftheSection(section:Int,allAttributesInRect:NSArray) -> CGFloat{ var totalLength:CGFloat = 0.0 totalLength = totalLength + (CGFloat (((self.collectionView?.numberOfItemsInSection(section))! - 1)) * padding) for attributes in allAttributesInRect { if(attributes.indexPath.section == section){ totalLength = totalLength + attributes.frame.width } } return totalLength } private func numberOfLines(allAttributesInRect:NSArray,section:Int)-> Int{ var totalLines:Int = 0 for attributes in allAttributesInRect { if(attributes.indexPath.section == section){ if (attributes.frame.origin.x == self.sectionInset.left){ totalLines = totalLines + 1 } } } return totalLines } private func sectionObjects(allAttributesInRect:NSArray,section:Int) -> NSMutableArray{ let objects:NSMutableArray = NSMutableArray() for attributes in allAttributesInRect { if(attributes.indexPath.section == section){ objects.addObject(attributes) } } return objects } private func lastRow(allAttributesInRect:NSArray,numberOfRows:Int,section:Int) -> NSMutableArray{ var totalLines:Int = 0 let lastRowArrays:NSMutableArray = NSMutableArray() for attributes in allAttributesInRect { if(attributes.indexPath.section == section){ if (attributes.frame.origin.x == self.sectionInset.left){ totalLines = totalLines + 1 if(totalLines == numberOfRows){ lastRowArrays.addObject(attributes) } } else{ if(totalLines == numberOfRows){ lastRowArrays.addObject(attributes) } } } } return lastRowArrays } private func setMiddleTheLastRow(lastRowAttrs:NSMutableArray)->NSMutableArray{ let networkingefinedValues = NSMutableArray() let totalLengthOftheView = self.collectionView?.frame.width var totalLenthOftheCells:CGFloat = 0.0 totalLenthOftheCells = totalLenthOftheCells + (CGFloat (lastRowAttrs.count) - 1) * padding for attrs in lastRowAttrs{ totalLenthOftheCells = totalLenthOftheCells + attrs.frame.width } var initalValue = (totalLengthOftheView!/2) - (totalLenthOftheCells/2) for i in 0..<lastRowAttrs.count { let changeingAttribute:UICollectionViewLayoutAttributes = lastRowAttrs[i] as! UICollectionViewLayoutAttributes var frame = changeingAttribute.frame frame.origin.x = initalValue changeingAttribute.frame = frame networkingefinedValues.addObject(changeingAttribute) initalValue = initalValue + changeingAttribute.frame.width + padding } return networkingefinedValues; } } 

Versión Swift 3.0 de la class:

 class UICollectionViewFlowCenterLayout: UICollectionViewFlowLayout { override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { guard let suggestedAttributes = super.layoutAttributesForElements(in: rect) else { return nil } guard scrollDirection == .vertical else { return suggestedAttributes } var newAttributes: [UICollectionViewLayoutAttributes] = [] var currentRowAttributes: [UICollectionViewLayoutAttributes] = [] var yOffset:CGFloat = sectionInset.top for attributes in suggestedAttributes { if attributes.frame.origin.y != yOffset { centerSingleRowWithItemsAttributes(attributes: &currentRowAttributes, rect: rect) newAttributes += currentRowAttributes currentRowAttributes = [] yOffset = attributes.frame.origin.y } currentRowAttributes += [attributes] } centerSingleRowWithItemsAttributes(attributes: &currentRowAttributes, rect: rect) newAttributes += currentRowAttributes return newAttributes } private func centerSingleRowWithItemsAttributes( attributes: inout [UICollectionViewLayoutAttributes], rect: CGRect) { guard let item = attributes.last else { return } let itemsCount = CGFloat(attributes.count) let sideInsets = rect.width - (item.frame.width * itemsCount) - (minimumInteritemSpacing * (itemsCount - 1)) var leftOffset = sideInsets / 2 for attribute in attributes { attribute.frame.origin.x = leftOffset leftOffset += attribute.frame.width + minimumInteritemSpacing } } }