如何交替基于行的UICollectiveViewCell的背景颜色?

我知道这是相当容易的UITableViewCells,但我不知道如何使用UICollectionView来解决这个问题。

编辑。 图片澄清。 单元格的文本内容在这里不一样,但应该是。 在风景中:

在肖像中:

在这里输入图像说明

我试图根据cellForItemAtIndexPath:方法中的索引path的行属性,将单元格的文本标签的颜色与单元格的背景颜色进行天真的切换。 但是索引path的行属性在UICollectionViewFlowLayout中并不是真正的行。

收集视图和其布局都不会告诉你一个项目的“行号”。 你必须自己计算一下。

如果你所有的测量都是统一的(如果你没有实现任何UICollectionViewDelegateFlowLayout方法就是这种情况),你可以很容易地进行计算。

有几个地方可以做计算和分配颜色。 我会告诉你这个“正确”的地方:在布局经理。

iOS版Collection View编程指南中的“了解何时对stream布局进行子类化”部分将告诉您需要做的基本事情,但这些内容是相当枯燥的,所以我会引导您完成。

注意:由于layoutAttributes是一个非常麻烦的标识符,我通常使用pose来代替。

UICollectionViewLayoutAttributes子类

首先,我们需要一种将布局pipe理器的背景颜色传递给单元格的方法。 正确的做法是通过UICollectionViewLayoutAttributes 。 界面只是添加一个属性:

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

实现需要实现NSCopying协议:

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

UICollectionViewCell子类

接下来,单元格需要使用backgroundColor属性。 你可能已经有了一个UICollectionViewCell子类。 你需要实现applyLayoutAttributes:在你的子类中,像这样:

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

UICollectionViewFlowLayout子类

现在您需要制作一个使用MyLayoutAttributes而不是UICollectionViewLayoutAttributesUICollectionViewFlowLayout的子类,并正确设置每个MyLayoutAttributesbackgroundColor 。 让我们定义一个接口,让它有一个属性,它是分配给行的颜色数组:

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

我们在实现中需要做的第一件事就是指定我们希望使用哪个布局属性类:

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

接下来,我们需要重写UICollectionViewLayout两个方法来设置每个姿势的backgroundColor属性。 我们呼吁super获得姿势的集合,然后使用辅助方法来设置背景颜色:

 - (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; } 

这是实际分配背景颜色的方法:

 - (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]; } } 

请注意,这个方法做了几个假设:

  • 它假定滚动方向是垂直的。
  • 它假定所有项目都是相同的大小。
  • 它假定所有部分具有相同的插入和行距。
  • 它假定每个部分至less有一个项目。

最后一个假设的原因是,一个部分中的所有行都跟着最小的行距, 除了最后一行,其后面是底部部分插入。 我需要知道当前项目行的前面有多less行。 我试图通过将Y坐标除以行的高度来做到这一点…但是每个部分的最后一行比其他行更短。 因此,欺骗。

应用你的新类

无论如何,现在我们需要把这些新的类用于使用。

首先,我们需要使用MyLayout而不是UICollectionViewFlowLayout 。 如果你在代码中设置你的集合视图,只需创build一个MyLayout而不是MyLayout并将其分配给集合视图。 如果你正在设置故事板,find集合视图的布局(作为集​​合视图的子集在故事板大纲中),并将其自定义类设置为MyLayout

其次,我们需要为布局分配一个颜色数组。 如果您正在使用故事板,则可能需要在viewDidLoad执行此操作。 您可以询问集合视图的布局,然后将其转换为MyLayout 。 我更喜欢使用正确types的sockets,连接到故事板中的布局对象。

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

结果

如果你的设置正确,你会得到这样的结果:

肖像屏幕截图

在横向方向看起来像这样:

风景屏幕快照

我把我的testing项目放在这个github仓库中 。

我不认为有没有任何好的一般方法来做到这一点,而不需要inheritancestream布局。 对于更具体的情况,您可以使用整数运算与模运算符结合,从indexPath中获取“rows”。 所以,如果你每行有5个项目,你可以从这个expression式返回0或1:

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

只要testing这个值,并相应地改变你的颜色。 当然,你必须改变旋转的expression式,因为每行都有不同数量的项目。

使用indexPath.section属性获取一行。

像这样的东西应该这样做。 可能需要一些调整,等等。假设你没有使用必须有一些变化的部分。

 #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]; } 

这很糟糕,如果有一个标题,可能会打破,但至less这会让你走。 在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;