ios - Espaciado celular en UICollectionView
objective-c (22)
¿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?
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 ...
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.
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
Código simple para espaciado
let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
layout.minimumInteritemSpacing = 10 // Some float value
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
.
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;
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.
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];
Intenta jugar con este método:
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView
layout:(UICollectionViewLayout*)collectionViewLayout
insetForSectionAtIndex:(NSInteger)section {
UIEdgeInsets insets = UIEdgeInsetsMake(?, ?, ?, ?);
return insets;
}
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):
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
}
La solución anterior de 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
}
}
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
}
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
Mi solución en el espaciado de línea celular Swift 3
como en Instagram:
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://.com/a/26962452/6013170
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;
}
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
}
Sé que el tema es antiguo, pero en caso de que alguien necesite una respuesta correcta, aquí lo que necesita:
- Anular el diseño de flujo estándar.
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.
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
}
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 .
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.
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
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.
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..<answer.count {
let currentAttributes = answer[i]
let previousAttributes = answer[i - 1]
let maximumSpacing: CGFloat = 8
let origin = previousAttributes.frame.maxX
if (origin + maximumSpacing + currentAttributes.frame.size.width < self.collectionViewContentSize.width && currentAttributes.frame.origin.x > previousAttributes.frame.origin.x) {
var frame = currentAttributes.frame
frame.origin.x = origin + maximumSpacing
currentAttributes.frame = frame
}
}
return answer
}
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
}
}
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
}
}