如何基于行替换UICollectionViewCell背景颜色?

时间:2013-08-19 15:20:31

标签: ios uicollectionview uicollectionviewlayout

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

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

纵向:

enter image description here

我尝试根据cellForItemAtIndexPath:方法中索引路径的row属性,天真地将单元格文本标签的颜色切换为单元格的背景颜色。但是,Index Path的row属性实际上不是UICollectionViewFlowLayout中的一行。

5 个答案:

答案 0 :(得分:9)

集合视图及其布局都不会告诉您项目的“行号”。你必须自己计算它。

如果您的所有测量结果都是一致的(如果您没有实施任何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的{​​{1}}子类,并正确设置每个UICollectionViewLayoutAttributes的{​​{1}}。让我们定义接口,使其具有一个属性,该属性是要分配给行的颜色数组:

backgroundColor

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

MyLayoutAttributes

接下来,我们需要覆盖@interface MyLayout : UICollectionViewFlowLayout @property (nonatomic, copy) NSArray *rowColors; @end 的两个方法来设置每个姿势的@implementation MyLayout + (Class)layoutAttributesClass { return [MyLayoutAttributes class]; } 属性。我们调用UICollectionViewLayout获取姿势集,然后使用辅助方法设置背景颜色:

backgroundColor

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

super

请注意,此方法有几个假设:

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

最后一个假设的原因是一个部分中的所有行后跟最小行间距,之外的最后一行,后面是底部插入。我需要知道当前项的行前面有多少行。我尝试通过将Y坐标除以行的高度来做到这一点......但每个部分的最后一行比其他部分短。因此捏造。

应用新课程

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

首先,我们需要使用- (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]; } } 。如果您要在代码中设置集合视图,只需创建一个而不是MyLayout并将其分配给集合视图。如果您在故事板中进行设置,请查找集合视图的布局(它在故事板大纲中作为集合视图的子级)并将其自定义类设置为UICollectionViewFlowLayout

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

viewDidLoad

结果

如果你正确设置了所有内容,你会得到如下结果:

portrait screen shot

并且在横向方向上它看起来像这样:

landscape screen shot

我已将测试项目in this github repository

答案 1 :(得分:1)

我认为没有子流程布局可以有任何好的通用方法。对于更具体的情况,您可以使用整数数学与模数运算符组合来从indexPath获取“行”。因此,如果每行有5个项目,则可以从此表达式返回0或1:

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

只需测试此值,然后相应地更改颜色。当然,您必须在旋转时更改表达式,因为每行上都有不同数量的项目。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

这样的事情应该这样做。可能需要一些调整等等。假设您没有使用必须进行一些更改的部分..

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

答案 4 :(得分:0)

这很令人讨厌,如果有一个标题会受到破坏,但至少这会让你前进。在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;