¿Cómo alterno el color de background de UICollectiveViewCell basado en la fila?

Sé que esto es bastante fácil para UITableViewCells pero no estoy seguro de cómo abordar esto usando una UICollectionView.

EDITAR. Imágenes para aclaración. El contenido de text de las celdas no es el mismo aquí, pero deberían serlo. En paisaje:

En retrato:

introduzca la descripción de la imagen aquí

Intenté cambiar ingenuamente el color de la label de text de mi celda con el color de background de la celda basado en la propiedad de fila de la ruta de índice en el método cellForItemAtIndexPath: Sin embargo, la propiedad de fila de Index Path no es realmente una fila en UICollectionViewFlowLayout.

Ni la vista de colección ni su layout le indicarán un "número de fila" para los artículos. Tienes que calcularlo tú mismo.

Si todas sus mediciones son uniformes (que es el caso si no implementó ningún método UICollectionViewDelegateFlowLayout ), puede calcularlo con bastante facilidad.

Hay algunos lugares donde puede hacer el cálculo y asignar el color. Te voy a mostrar el lugar "adecuado" para hacerlo: en el administrador de layout.

La sección "Saber cuándo subclamar el layout del flujo" de la Guía de progtwigción de la Colección View para iOS te dice las cosas básicas que debes hacer, pero es bastante simple, así que te guiaré a través de ella.

Nota: dado que layoutAttributes es un identificador bastante engorroso, generalmente uso pose lugar.

UICollectionViewLayoutAttributes subclass

En primer lugar, necesitamos una forma de pasar el color de background del gestor de layout a la celda. La forma correcta de hacerlo es UICollectionViewLayoutAttributes . La interfaz solo agrega una propiedad:

 @interface MyLayoutAttributes : UICollectionViewLayoutAttributes @property (nonatomic, strong) UIColor *backgroundColor; @end 

La implementación debe implementar el protocolo NSCopying :

 @implementation MyLayoutAttributes - (instancetype)copyWithZone:(NSZone *)zone { MyLayoutAttributes *copy = [super copyWithZone:zone]; copy.backgroundColor = self.backgroundColor; return copy; } @end 

Subclass UICollectionViewCell

Luego, la celda necesita usar esos attributes backgroundColor . Probablemente ya tenga una subclass UICollectionViewCell . applyLayoutAttributes: implementar applyLayoutAttributes: en su subclass, así:

 @implementation MyCell - (void)applyLayoutAttributes:(MyLayoutAttributes *)pose { [super applyLayoutAttributes:pose]; if (!self.backgroundView) { self.backgroundView = [[UIView alloc] init]; } self.backgroundView.backgroundColor = pose.backgroundColor; } @end 

UICollectionViewFlowLayout subclass

Ahora necesita crear una subclass de UICollectionViewFlowLayout que use MyLayoutAttributes lugar de UICollectionViewLayoutAttributes , y establece el MyLayoutAttributes de backgroundColor de cada MyLayoutAttributes correctamente. Definamos la interfaz para tener una propiedad que es la matriz de colors para asignar a las filas:

 @interface MyLayout : UICollectionViewFlowLayout @property (nonatomic, copy) NSArray *rowColors; @end 

Lo primero que debemos hacer en nuestra implementación es especificar qué class de attributes de layout queremos que use:

 @implementation MyLayout + (Class)layoutAttributesClass { return [MyLayoutAttributes class]; } 

Luego, debemos anular dos methods de UICollectionViewLayout para establecer la propiedad backgroundColor de cada pose. Llamamos a super para get el set de poses, y luego usamos un método auxiliar para establecer los colors de background:

 - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect { NSArray *poses = [super layoutAttributesForElementsInRect:rect]; [self assignBackgroundColorsToPoses:poses]; return poses; } - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath { MyLayoutAttributes *pose = (MyLayoutAttributes *)[super layoutAttributesForItemAtIndexPath:indexPath]; [self assignBackgroundColorsToPoses:@[ pose ]]; return pose; } 

Aquí está el método que realmente asigna los colors de background:

 - (void)assignBackgroundColorsToPoses:(NSArray *)poses { NSArray *rowColors = self.rowColors; int rowColorsCount = rowColors.count; if (rowColorsCount == 0) return; UIEdgeInsets insets = self.sectionInset; CGFloat lineSpacing = self.minimumLineSpacing; CGFloat rowHeight = self.itemSize.height + lineSpacing; for (MyLayoutAttributes *pose in poses) { CGFloat y = pose.frame.origin.y; NSInteger section = pose.indexPath.section; y -= section * (insets.top + insets.bottom) + insets.top; y += section * lineSpacing; // Fudge: assume each prior section had at least one cell int row = floorf(y / rowHeight); pose.backgroundColor = rowColors[row % rowColorsCount]; } } 

Tenga en count que este método hace varias suposiciones:

  • Asume que la dirección de desplazamiento es vertical.
  • Asume que todos los artículos tienen el mismo tamaño.
  • Asume que todas las secciones tienen las mismas inserciones y espaciamientos de línea.
  • Asume que cada sección tiene al less un elemento.

La razón de la última suposition es que todas las filas de una sección están seguidas por el espaciado de línea mínimo, excepto por la última fila, que es seguida por la inserción de la sección inferior. Necesito saber cuántas filas precedieron a la fila del elemento actual. Trato de hacerlo dividiendo la coorderada Y por el alto de una fila … pero la última fila de cada sección es más corta que las otras. De ahí el fudging.

Aplicando sus nuevas classs

De todos modos, ahora necesitamos usar estas nuevas classs.

Primero, necesitamos usar MyLayout lugar de UICollectionViewFlowLayout . Si está configurando su vista de colección en código, simplemente cree una en lugar de MyLayout y asígnela a la vista de colección. Si está configurando cosas en un guión gráfico, encuentre el layout de la vista de la colección (está en el outliner del guión gráfico como un elemento secundario de la vista de la colección) y configure su class personalizada en MyLayout .

Segundo, tenemos que asignar una matriz de colors al layout. Si está utilizando un storyboard, probablemente quiera hacerlo en viewDidLoad . Puede pedirle a la vista de colección su layout y luego MyLayout a MyLayout . Prefiero usar una salida del tipo adecuado, conectada al object de layout en el guión gráfico.

 @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.layout.rowColors = @[ [UIColor lightGrayColor], [UIColor cyanColor], ]; } 

Resultado

Si tienes todo configurado correctamente, obtendrás un resultado como este:

retrato, pantalla, tiro

y en orientación horizontal se ve así:

paisaje, pantalla, tiro

He puesto mi proyecto de testing en este repository github .

No creo que haya una buena forma general de hacer esto sin subclasificar el layout del flujo. Para un caso más específico, puede usar las matemáticas enteras combinadas con el operador de module para get "filas" de la ruta de índice. Entonces, si tuviera 5 elementos en cada fila, podría devolver 0 o 1 de esta expresión:

 NSInteger rowTest = (indexPath.row / 5) % 2; 

Solo testing este valor y cambia tu color en consecuencia. Por supuesto, tendrá que cambiar la expresión en rotation, ya que tendrá una cantidad diferente de elementos en cada fila.

Utilice la propiedad indexPath.section para get una fila.

Algo así debería hacerlo. Puede tomar algunos retoques, etc. Suponiendo que no está utilizando secciones que deberían tener algunos cambios.

 #import <UIKit/UIKit.h> @interface ViewController : UIViewController <UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout> @end 

 #import "ViewController.h" #define WIDTH 200 #define HEIGHT 200 #define XMARGIN 50 #define YMARGIN 20 @interface ViewController () @property (weak, nonatomic) IBOutlet UICollectionView *collectionView; @property (assign, nonatomic) int numberOfCols; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; UINib* cellNib = [UINib nibWithNibName:@"CollectionCell" bundle:nil]; [self.collectionView registerNib:cellNib forCellWithReuseIdentifier:@"CollectionCell"]; } - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { return 1; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return 6; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"CollectionCell" forIndexPath:indexPath]; NSLog(@"Row number %d",indexPath.row); int rem = (indexPath.row / self.numberOfCols); NSLog(@"col number %d", rem); if ( rem % 2 == 0 ) cell.backgroundColor = [UIColor yellowColor]; else cell.backgroundColor = [UIColor greenColor]; return cell; } #pragma mark – UICollectionViewDelegateFlowLayout - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { self.numberOfCols = (collectionView.frame.size.width - (XMARGIN*2)) / (WIDTH); NSLog(@"Number of cols is %d",self.numberOfCols); return CGSizeMake(WIDTH,HEIGHT); } - (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section { return UIEdgeInsetsMake(XMARGIN, YMARGIN, XMARGIN, YMARGIN); } - (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation { [self.collectionView reloadData]; } 

Esto es asqueroso, sujeto a romperse si hay un encabezado, pero al less esto lo hará funcionar. En cellForItemAtIndexPath …

 // dequeue cell CGFloat originY = cell.frame.origin.y; // if there are headers, then grab the header view and... originY -= header.bounds.size.height * indexPath.section; NSInteger row = originY / cell.bounds.size.height; BOOL evenRow = row%2 == 0;