我知道这对于 UITableViewCells 来说相当容易,但我不确定如何使用 UICollectionView 来解决这个问题。
编辑。图片以供澄清。单元格的文本内容在这里不一样,但它们应该是一样的。在风景中:
纵向:
我试图根据cellForItemAtIndexPath:
方法中索引路径的 row 属性,用单元格的背景颜色天真地切换单元格文本标签的颜色。然而,Index Path 的 row 属性在 UICollectionViewFlowLayout 中并不是真正的一行。
我知道这对于 UITableViewCells 来说相当容易,但我不确定如何使用 UICollectionView 来解决这个问题。
编辑。图片以供澄清。单元格的文本内容在这里不一样,但它们应该是一样的。在风景中:
纵向:
我试图根据cellForItemAtIndexPath:
方法中索引路径的 row 属性,用单元格的背景颜色天真地切换单元格文本标签的颜色。然而,Index Path 的 row 属性在 UICollectionViewFlowLayout 中并不是真正的一行。
集合视图及其布局都不会告诉您项目的“行号”。你必须自己计算。
如果你所有的测量都是统一的(如果你没有实现任何UICollectionViewDelegateFlowLayout
方法就是这种情况),你可以很容易地计算它。
有几个地方可以进行计算并分配颜色。我将向您展示执行此操作的“正确”位置:在布局管理器中。
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
,并正确设置backgroundColor
每个的子类MyLayoutAttributes
。让我们将接口定义为具有一个属性,该属性是要分配给行的颜色数组:
@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];
}
}
请注意,此方法做了几个假设:
最后一个假设的原因是,一个节中的所有行后面都跟着最小行距,除了最后一行,后面是底部的节插图。我需要知道当前项目行之前有多少行。我尝试通过将 Y 坐标除以一行的高度来做到这一点……但每个部分的最后一行比其他部分短。因此,捏造。
无论如何,现在我们需要使用这些新类。
首先,我们需要使用MyLayout
而不是UICollectionViewFlowLayout
. 如果您在代码中设置集合视图,只需创建一个代替MyLayout
并将其分配给集合视图。如果您在情节提要中进行设置,请找到集合视图的布局(它在情节提要大纲中作为集合视图的子级)并将其自定义类设置为MyLayout
.
其次,我们需要为布局分配一组颜色。如果您正在使用情节提要,您可能希望在viewDidLoad
. 您可以向集合视图询问其布局,然后将其转换为MyLayout
. 我更喜欢使用正确类型的插座,连接到情节提要中的布局对象。
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.layout.rowColors = @[
[UIColor lightGrayColor],
[UIColor cyanColor],
];
}
如果一切设置正确,您将得到如下结果:
在横向方向它看起来像这样:
我已经将我的测试项目放在了这个 github 存储库中。
我不认为有任何好的通用方法可以在不继承流布局的情况下做到这一点。对于更具体的情况,您可以将整数数学与模运算符结合使用以从 indexPath 获取“行”。因此,如果每行有 5 个项目,则可以从此表达式返回 0 或 1:
NSInteger rowTest = (indexPath.row / 5) % 2;
只需测试这个值,并相应地改变你的颜色。当然,您必须更改旋转表达式,因为每行上的项目数量不同。
使用 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];
}
这很糟糕,如果有标题,可能会被打破,但至少这会让你继续前进。在 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;