如何根据行替换 UICollectiveViewCell 的背景颜色?
Posted
技术标签:
【中文标题】如何根据行替换 UICollectiveViewCell 的背景颜色?【英文标题】:How do I alternate a UICollectiveViewCell's background color based on row? 【发布时间】:2013-08-19 15:20:30 【问题描述】:我知道这对于 UITableViewCells 来说相当容易,但我不确定如何使用 UICollectionView 来解决这个问题。
编辑。图片以供澄清。单元格的文本内容在这里不一样,但它们应该是一样的。 横向:
纵向:
我尝试根据cellForItemAtIndexPath:
方法中的索引路径的行属性天真地将单元格文本标签的颜色与单元格的背景颜色进行切换。然而,Index Path 的 row 属性在 UICollectionViewFlowLayout 中并不是真正的一行。
【问题讨论】:
你能详细介绍一下你想要实现的模式吗? 每行的项目数是恒定的吗?您是否允许轮换,如果允许,是否会改变每行的项目数? @0x7fffffff 第一个是一种颜色,我网格中的第二行不同,第三行与第一行相同,等等。 @rdelmar 是的,除了可能是最后一行。是的,轮换会改变每行的项目数。 @s1m0n 你知道 item 和 row 除了应该与哪个视图一起使用之外还有什么不同吗?它们似乎总是返回相同的值。 【参考方案1】:集合视图和它的布局都不会告诉你项目的“行号”。您必须自己计算。
如果您的所有测量值都是统一的(如果您没有实现任何 UICollectionViewDelegateFlowLayout
方法就是这种情况),您可以很容易地计算它。
有几个地方可以进行计算并分配颜色。我将向您展示执行此操作的“正确”位置:在布局管理器中。
“Knowing When to Subclass the Flow Layout” section of the Collection View Programming Guide for ios 告诉你需要做的基本事情,但它非常简单,所以我将引导你完成它。
注意:由于layoutAttributes
是一个相当麻烦的标识符,我通常使用pose
来代替。
UICollectionViewLayoutAttributes
子类
首先,我们需要一种将背景颜色从布局管理器传递到单元格的方法。正确的做法是继承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
子类
现在您需要创建一个UICollectionViewFlowLayout
的子类,它使用MyLayoutAttributes
而不是UICollectionViewLayoutAttributes
,并正确设置每个MyLayoutAttributes
的backgroundColor
。让我们将接口定义为具有一个属性,该属性是分配给行的颜色数组:
@interface MyLayout : UICollectionViewFlowLayout
@property (nonatomic, copy) NSArray *rowColors;
@end
我们在实现中需要做的第一件事是指定我们希望它使用的布局属性类:
@implementation MyLayout
+ (Class)layoutAttributesClass
return [MyLayoutAttributes class];
接下来,我们需要重写UICollectionViewLayout
的两个方法来设置每个pose的backgroundColor
属性。我们调用super
来获取pose集合,然后使用辅助方法设置背景颜色:
- (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];
请注意,此方法做了几个假设:
它假定滚动方向是垂直的。 假定所有项目的大小相同。 它假定所有部分都具有相同的插入和行距。 假设每个部分至少有一项。最后一个假设的原因是一个section中的所有行后面都有最小行间距,除了是最后一行,后面是底部的section inset。我需要知道当前项目行之前有多少行。我尝试通过将 Y 坐标除以一行的高度来做到这一点……但每个部分的最后一行比其他部分短。因此,捏造。
应用你的新类
无论如何,现在我们需要使用这些新类。
首先,我们需要使用MyLayout
而不是UICollectionViewFlowLayout
。如果您在代码中设置集合视图,只需创建一个而不是 MyLayout
并将其分配给集合视图。如果您在情节提要中进行设置,请找到集合视图的布局(它在情节提要大纲中作为集合视图的子级)并将其自定义类设置为 MyLayout
。
其次,我们需要为布局分配一组颜色。如果您使用故事板,您可能希望在viewDidLoad
中执行此操作。您可以向集合视图询问其布局,然后将其转换为MyLayout
。我更喜欢使用正确类型的插座,连接到情节提要中的布局对象。
@implementation ViewController
- (void)viewDidLoad
[super viewDidLoad];
self.layout.rowColors = @[
[UIColor lightGrayColor],
[UIColor cyanColor],
];
结果
如果一切设置正确,您将得到如下结果:
横向显示如下:
我已经把我的测试项目in this github repository。
【讨论】:
这正是我正在寻找的。谢谢! 很好解释的答案!【参考方案2】:我认为在不继承流布局的情况下,有什么好的通用方法可以做到这一点。对于更具体的情况,您可以将整数数学与模运算符结合使用以从 indexPath 获取“行”。因此,如果每行有 5 个项目,则可以从此表达式返回 0 或 1:
NSInteger rowTest = (indexPath.row / 5) % 2;
只需测试此值,并相应地更改您的颜色。当然,您必须更改轮换表达式,因为每行上的项目数量不同。
【讨论】:
【参考方案3】:使用 indexPath.section 属性获取一行。
【讨论】:
我可以做到这一点,尽管我正在寻找一种可重复使用的方法,这种方法不涉及更改我现有的部分,并且一行中的项目数会根据轮换发生变化。跨度> 我不清楚你的意思。您能否详细说明您的评论。 想象一下,我的收藏视图中有九个单元格,横向显示为 3 x 3 网格。我每个部分可以有 3 个单元格,但是当我将其旋转为纵向时,我的集体视图被绘制为两列网格,每个部分的最后一行缺少一个单元格,然后颜色将不基于行。 这是否意味着一旦分配给单元格的颜色无论方向如何都应该保持不变? 如果方向改变每行的单元格数,颜色与方向间接相关。我添加了图片。【参考方案4】:应该这样做。可能需要一些调整等。假设您没有使用必须进行一些更改的部分..
#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];
【讨论】:
【参考方案5】:这很糟糕,如果有标题,可能会被破坏,但至少这会让你继续前进。在 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;
【讨论】:
以上是关于如何根据行替换 UICollectiveViewCell 的背景颜色?的主要内容,如果未能解决你的问题,请参考以下文章