Espaciado celular en UICollectionView

¿Cómo configuro el espaciamiento celular en una sección de UICollectionView ? Sé que hay una propiedad minimumInteritemSpacing La configuré en 5.0, pero el espaciado no aparece 5.0. Implementé el método de delegación de flujo.

 - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { return 5.0; } 

todavía no estoy obteniendo el resultado deseado. Creo que es el espacio mínimo. ¿No hay forma de que pueda establecer el espaciado máximo?

simulador sc

Sé que el tema es antiguo, pero en caso de que alguien necesite una respuesta correcta, aquí lo que necesita:

  1. Anular el diseño de flujo estándar.
  2. Agregue implementación así:

     - (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect { NSArray *answer = [super layoutAttributesForElementsInRect:rect]; for(int i = 1; i < [answer count]; ++i) { UICollectionViewLayoutAttributes *currentLayoutAttributes = answer[i]; UICollectionViewLayoutAttributes *prevLayoutAttributes = answer[i - 1]; NSInteger maximumSpacing = 4; NSInteger origin = CGRectGetMaxX(prevLayoutAttributes.frame); if(origin + maximumSpacing + currentLayoutAttributes.frame.size.width < self.collectionViewContentSize.width) { CGRect frame = currentLayoutAttributes.frame; frame.origin.x = origin + maximumSpacing; currentLayoutAttributes.frame = frame; } } return answer; } 

donde maximumSpacing se puede establecer en cualquier valor que prefiera. Este truco garantiza que el espacio entre las celdas sería EXACTAMENTE igual al espacio máximo.

Apoyando la pregunta inicial. Traté de obtener el espaciado de 5px en UICollectionView pero esto no funciona, así como con UIEdgeInsetsMake(0,0,0,0)

En una UITableView puedo hacer esto especificando directamente las coordenadas x, y en una fila …

enter image description here

Aquí está mi código UICollectionView:

 #pragma mark collection view cell layout / size - (CGSize)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { return [self getCellSize:indexPath]; // will be w120xh100 or w190x100 // if the width is higher, only one image will be shown in a line } #pragma mark collection view cell paddings - (UIEdgeInsets)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { return UIEdgeInsetsMake(0, 0, 0, 0); // top, left, bottom, right } - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { return 5.0; } 

Actualización: resolvió mi problema con el siguiente código.

enter image description here

ViewController.m

 #import "ViewController.h" #import "MagazineCell.h" // created just the default class. static NSString * const cellID = @"cellID"; @interface ViewController () @end @implementation ViewController #pragma mark - Collection view -(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { return 1; } -(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return 30; } -(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { MagazineCell *mCell = (MagazineCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath]; mCell.backgroundColor = [UIColor lightGrayColor]; return mCell; } #pragma mark Collection view layout things // Layout: Set cell size - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { NSLog(@"SETTING SIZE FOR ITEM AT INDEX %d", indexPath.row); CGSize mElementSize = CGSizeMake(104, 104); return mElementSize; } - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section { return 2.0; } - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { return 2.0; } // Layout: Set Edges - (UIEdgeInsets)collectionView: (UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { // return UIEdgeInsetsMake(0,8,0,8); // top, left, bottom, right return UIEdgeInsetsMake(0,0,0,0); // top, left, bottom, right } @end 

Usando un diseño de flujo horizontal, también obtuve un espacio de 10 puntos entre las celdas. Para eliminar el espaciado que necesitaba para establecer minimumLineSpacing , así como minimumInterItemSpacing a cero.

 UICollectionViewFlowLayout *flow = [[UICollectionViewFlowLayout alloc] init]; flow.itemSize = CGSizeMake(cellWidth, cellHeight); flow.scrollDirection = UICollectionViewScrollDirectionHorizontal; flow.minimumInteritemSpacing = 0; flow.minimumLineSpacing = 0; 

Además, si todas sus celdas tienen el mismo tamaño, es más simple y más eficiente establecer la propiedad en el diseño de flujo directamente en lugar de usar métodos de delegado.

Recuerde, es el espacio de línea mínimo , no el espaciado entre elementos mínimo o el espacio de celda. Porque la dirección de desplazamiento de su collectionView es HORIZONTAL .

Si era vertical, entonces necesita establecer el espacio de la celda o el espacio entre elementos para el espacio horizontal entre las celdas y el espaciado entre líneas para el espacio vertical entre las celdas.

Versión Objective-C

 - (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section { return 20; } 

Versión Swift:

 func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return 20 } 

Pruebe este código para asegurarse de tener un espacio de 5px entre cada elemento:

 - (UIEdgeInsets)collectionView:(UICollectionView *) collectionView layout:(UICollectionViewLayout *) collectionViewLayout insetForSectionAtIndex:(NSInteger) section { return UIEdgeInsetsMake(0, 0, 0, 5); // top, left, bottom, right } - (CGFloat)collectionView:(UICollectionView *) collectionView layout:(UICollectionViewLayout *) collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger) section { return 5.0; } 

Swift versión de la respuesta más popular. El espacio entre las celdas será igual a cellSpacing .

 class CustomViewFlowLayout : UICollectionViewFlowLayout { let cellSpacing:CGFloat = 4 override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { if let attributes = super.layoutAttributesForElementsInRect(rect) { for (index, attribute) in attributes.enumerate() { if index == 0 { continue } let prevLayoutAttributes = attributes[index - 1] let origin = CGRectGetMaxX(prevLayoutAttributes.frame) if(origin + cellSpacing + attribute.frame.size.width < self.collectionViewContentSize().width) { attribute.frame.origin.x = origin + cellSpacing } } return attributes } return nil } } 

He encontrado una manera muy fácil de configurar el espaciado entre celdas o filas mediante el uso de IB.

Simplemente seleccione UICollectionView del storyboard / archivo Xib y haga clic en Size Inspector como se especifica en la imagen siguiente.

enter image description here

Para configurar el espacio de forma programática, use las siguientes propiedades.

1) Para establecer el espacio entre las filas.

 [self.collectionView setMinimumLineSpacing:5]; 

2) Para establecer el espacio entre elementos / celdas.

 [self.collectionView setMinimumInteritemSpacing:5]; 

Tenga en cuenta el nombre de la propiedad minimumInterItemSpacing . Este será el espacio mínimo entre los elementos, no el espaciado exacto . Si establece minimumInterItemSpacing en algún valor, puede asegurarse de que el espaciado no sea un valor menor que ese. Pero existe la posibilidad de obtener un mayor valor.

En realidad, el espacio entre elementos depende de varios factores itemSize y sectionInset . La vista de colección coloca dinámicamente los contenidos en función de estos valores. Entonces no puedes asegurar el espaciado exacto. Debe realizar algunas pruebas y errores con sectionInset y minimumInterItemSpacing .

Respuesta para Swift 3.0, Xcode 8

1. Asegúrese de configurar el delegado de vista de colección

 class DashboardViewController: UIViewController { @IBOutlet weak var dashboardCollectionView: UICollectionView! override func viewDidLoad() { super.viewDidLoad() dashboardCollectionView.delegate = self } } 

2. Implementar el protocolo UICollectionViewDelegateFlowLayout , no UICollectionViewDelegate.

 extension DashboardViewController: UICollectionViewDelegateFlowLayout { fileprivate var sectionInsets: UIEdgeInsets { return .zero } fileprivate var itemsPerRow: CGFloat { return 2 } fileprivate var interitemSpace: CGFloat { return 5.0 } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let sectionPadding = sectionInsets.left * (itemsPerRow + 1) let interitemPadding = max(0.0, itemsPerRow - 1) * interitemSpace let availableWidth = collectionView.bounds.width - sectionPadding - interitemPadding let widthPerItem = availableWidth / itemsPerRow return CGSize(width: widthPerItem, height: widthPerItem) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { return sectionInsets } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return 0.0 } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { return interitemSpace } } 

Defina el protocolo UICollectionViewDelegateFlowLayout en su archivo de encabezado.

Implemente el siguiente método del protocolo UICollectionViewDelegateFlowLayout de la UICollectionViewDelegateFlowLayout manera:

 - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { return UIEdgeInsetsMake(5, 5, 5, 5); } 

Haga clic aquí para ver la documentación de Apple del método UIEdgeInsetMake .

La respuesta (y también la versión rápida ) tiene un pequeño problema: habrá un gran espacio a la derecha.

Esto se debe a que el diseño del flujo se personaliza para hacer el espaciado celular exacto, con un comportamiento de flotación a la izquierda .

Mi solución es manipular el recuadro de la sección, de modo que la sección se alinee en el centro, pero el espaciado es exactamente el especificado.

En la captura de pantalla a continuación, el espaciado entre artículos / líneas es exactamente de 8 puntos, mientras que la sección de lados izquierdo y derecho será mayor que 8 puntos (para que quede alineado en el centro):

celdas espaciadas uniformemente

Swift código como tal:

 private let minItemSpacing: CGFloat = 8 private let itemWidth: CGFloat = 100 private let headerHeight: CGFloat = 32 override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // Create our custom flow layout that evenly space out the items, and have them in the center let layout = UICollectionViewFlowLayout() layout.itemSize = CGSize(width: itemWidth, height: itemWidth) layout.minimumInteritemSpacing = minItemSpacing layout.minimumLineSpacing = minItemSpacing layout.headerReferenceSize = CGSize(width: 0, height: headerHeight) // Find n, where n is the number of item that can fit into the collection view var n: CGFloat = 1 let containerWidth = collectionView.bounds.width while true { let nextN = n + 1 let totalWidth = (nextN*itemWidth) + (nextN-1)*minItemSpacing if totalWidth > containerWidth { break } else { n = nextN } } // Calculate the section inset for left and right. // Setting this section inset will manipulate the items such that they will all be aligned horizontally center. let inset = max(minItemSpacing, floor( (containerWidth - (n*itemWidth) - (n-1)*minItemSpacing) / 2 ) ) layout.sectionInset = UIEdgeInsets(top: minItemSpacing, left: inset, bottom: minItemSpacing, right: inset) collectionView.collectionViewLayout = layout } 

Código simple para espaciado

 let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout layout.minimumInteritemSpacing = 10 // Some float value 

Estoy usando monotouch, por lo que los nombres y el código serán un poco diferentes, pero puedes hacerlo asegurándote de que el ancho de la vista de colección sea igual a (x * ancho de celda) + (x-1) * MinimumSpacing con x = cantidad de celdas por fila

Solo siga los pasos basados ​​en su MinimumInteritemSpacing y el ancho de la celda

1) Calculamos la cantidad de elementos por fila en función del tamaño de la celda + inserciones actuales + espaciado mínimo

 float currentTotalWidth = CollectionView.Frame.Width - Layout.SectionInset.Left - Layout.SectionInset.Right (Layout = flowlayout) int amountOfCellsPerRow = (currentTotalWidth + MinimumSpacing) / (cell width + MinimumSpacing) 

2) Ahora tiene toda la información para calcular el ancho esperado para la vista de colección

 float totalWidth =(amountOfCellsPerRow * cell width) + (amountOfCellsPerRow-1) * MinimumSpacing 

3) Entonces la diferencia entre el ancho actual y el ancho esperado es

 float difference = currentTotalWidth - totalWidth; 

4) Ahora ajuste las inserciones (en este ejemplo, lo agregamos a la derecha, por lo que la posición de la izquierda de la vista de colección permanece igual

 Layout.SectionInset.Right = Layout.SectionInset.Right + difference; 

Me encontré con un problema similar al de OP. Desafortunadamente, la respuesta aceptada no funcionó para mí, ya que el contenido de collectionView no se centraría correctamente. Por lo tanto, se me ocurrió una solución diferente que solo requiere que todos los elementos en la collectionView tengan el mismo ancho , lo que parece ser el caso en la pregunta:

 #define cellSize 90 - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { float width = collectionView.frame.size.width; float spacing = [self collectionView:collectionView layout:collectionViewLayout minimumInteritemSpacingForSectionAtIndex:section]; int numberOfCells = (width + spacing) / (cellSize + spacing); int inset = (width + spacing - numberOfCells * (cellSize + spacing) ) / 2; return UIEdgeInsetsMake(0, inset, 0, inset); } 

Ese código asegurará que el valor devuelto por ...minimumInteritemSpacing... será el espaciado exacto entre cada collectionViewCell y además garantizará que todas las celdas estarán centradas en la collectionView

La solución anterior de vojtech-vrbka es correcta pero desencadena una advertencia:

advertencia: UICollectionViewFlowLayout tiene incompatibilidad de marcos en caché para la ruta de índice – valor almacenado en caché: Esto es probable porque la disposición de la subclase de diseño de flujo es atributos de modificación devueltos por UICollectionViewFlowLayout sin copiarlos

El siguiente código debería arreglarlo:

 class CustomViewFlowLayout : UICollectionViewFlowLayout { let cellSpacing:CGFloat = 4 override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? { let original = super.layoutAttributesForElementsInRect(rect) if let original = original { let attributes = NSArray.init(array: original, copyItems: true) as! [UICollectionViewLayoutAttributes] for (index, attribute) in attributes.enumerate() { if index == 0 { continue } let prevLayoutAttributes = attributes[index - 1] let origin = CGRectGetMaxX(prevLayoutAttributes.frame) if(origin + cellSpacing + attribute.frame.size.width < self.collectionViewContentSize().width) { attribute.frame.origin.x = origin + cellSpacing } } return attributes } return nil } } 

Versión de Swift 3

Simplemente crea una subclase UICollectionViewFlowLayout y pega este método.

 override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { guard let answer = super.layoutAttributesForElements(in: rect) else { return nil } for i in 1.. previousAttributes.frame.origin.x) { var frame = currentAttributes.frame frame.origin.x = origin + maximumSpacing currentAttributes.frame = frame } } return answer } 

Si desea ajustar el espacio sin tocar el tamaño real de la celda, esta es la solución que mejor funcionó para mí. #xcode 9 # tvOS11 # iOS11 #swift

Entonces, en UICollectionViewDelegateFlowLayout , cambie implementar los métodos siguientes, el truco es que debe usar ambos, y la documentación realmente no me indica que piense en esa dirección. :RE

 open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { return cellSpacing } public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return cellSpacing } 

Tengo UICollectionView horizontal y UICollectionViewFlowLayout subclasificado. La vista de colección tiene celdas grandes, y solo muestra una fila de ellas a la vez, y la vista de colección se ajusta al ancho de la pantalla.

Intenté la respuesta de iago849 y funcionó, pero luego descubrí que ni siquiera necesitaba su respuesta. Por alguna razón, configurar el minimumInterItemSpacing no hace nada. El espaciado entre mis elementos / celdas puede controlarse completamente con minimumLineSpacing .

No estoy seguro de por qué funciona de esta manera, pero funciona.

Las versiones anteriores realmente no funcionaban con secciones> 1. Así que mi solución se encontró aquí https://codentrick.com/create-a-tag-flow-layout-with-uicollectionview/ . Para los perezosos:

 override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { let attributesForElementsInRect = super.layoutAttributesForElements(in: rect) var newAttributesForElementsInRect = [UICollectionViewLayoutAttributes]() // use a value to keep track of left margin var leftMargin: CGFloat = 0.0; for attributes in attributesForElementsInRect! { let refAttributes = attributes // assign value if next row if (refAttributes.frame.origin.x == self.sectionInset.left) { leftMargin = self.sectionInset.left } else { // set x position of attributes to current margin var newLeftAlignedFrame = refAttributes.frame newLeftAlignedFrame.origin.x = leftMargin refAttributes.frame = newLeftAlignedFrame } // calculate new value for current margin leftMargin += refAttributes.frame.size.width + 10 newAttributesForElementsInRect.append(refAttributes) } return newAttributesForElementsInRect } 

Tengo un problema con la respuesta aceptada, por lo que la actualicé, esto está funcionando para mí:

.h

 @interface MaxSpacingCollectionViewFlowLayout : UICollectionViewFlowLayout @property (nonatomic,assign) CGFloat maxCellSpacing; @end 

.metro

 @implementation MaxSpacingCollectionViewFlowLayout - (NSArray *) layoutAttributesForElementsInRect:(CGRect)rect { NSArray *attributes = [super layoutAttributesForElementsInRect:rect]; if (attributes.count < = 0) return attributes; CGFloat firstCellOriginX = ((UICollectionViewLayoutAttributes *)attributes[0]).frame.origin.x; for(int i = 1; i < attributes.count; i++) { UICollectionViewLayoutAttributes *currentLayoutAttributes = attributes[i]; if (currentLayoutAttributes.frame.origin.x == firstCellOriginX) { // The first cell of a new row continue; } UICollectionViewLayoutAttributes *prevLayoutAttributes = attributes[i - 1]; CGFloat prevOriginMaxX = CGRectGetMaxX(prevLayoutAttributes.frame); if ((currentLayoutAttributes.frame.origin.x - prevOriginMaxX) > self.maxCellSpacing) { CGRect frame = currentLayoutAttributes.frame; frame.origin.x = prevOriginMaxX + self.maxCellSpacing; currentLayoutAttributes.frame = frame; } } return attributes; } @end 

Mi solución en el espaciado de línea celular Swift 3 como en Instagram:

enter image description here

 lazy var collectionView: UICollectionView = { let layout = UICollectionViewFlowLayout() let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) cv.backgroundColor = UIColor.rgb(red: 227, green: 227, blue: 227) cv.showsVerticalScrollIndicator = false layout.scrollDirection = .vertical layout.minimumLineSpacing = 1 layout.minimumInteritemSpacing = 1 return cv }() func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { switch UIDevice.current.modelName { case "iPhone 4": return CGSize(width: 106, height: 106) case "iPhone 5": return CGSize(width: 106, height: 106) case "iPhone 6,7": return CGSize(width: 124, height: 124) case "iPhone Plus": return CGSize(width: 137, height: 137) default: return CGSize(width: frame.width / 3, height: frame.width / 3) } } 

Cómo detectar el dispositivo programmaticlly: https://stackoverflow.com/a/26962452/6013170

He intentado con la respuesta de iago849 y funcionó.

Swift 4

 open override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { guard let answer = super.layoutAttributesForElements(in: rect) else { return nil } let count = answer.count for i in 1.. prevLayoutAttributes.frame.origin.x { var frame = currentLayoutAttributes.frame frame.origin.x = origin + CGFloat(spacing) currentLayoutAttributes.frame = frame } } return answer } 

Aquí está el enlace para el proyecto github. https://github.com/vishalwaka/MultiTags

Intenta jugar con este método:

 - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { UIEdgeInsets insets = UIEdgeInsetsMake(?, ?, ?, ?); return insets; }