6

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

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

纵向:

在此处输入图像描述

我试图根据cellForItemAtIndexPath:方法中索引路径的 row 属性,用单元格的背景颜色天真地切换单元格文本标签的颜色。然而,Index Path 的 row 属性在 UICollectionViewFlowLayout 中并不是真正的一行。

4

5 回答 5

11

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

如果你所有的测量都是统一的(如果你没有实现任何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 存储库中。

于 2013-08-19T22:29:43.810 回答
1

我不认为有任何好的通用方法可以在不继承流布局的情况下做到这一点。对于更具体的情况,您可以将整数数学与模运算符结合使用以从 indexPath 获取“行”。因此,如果每行有 5 个项目,则可以从此表达式返回 0 或 1:

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

只需测试这个值,并相应地改变你的颜色。当然,您必须更改旋转表达式,因为每行上的项目数量不同。

于 2013-08-19T15:51:48.610 回答
0

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

于 2013-08-19T15:32:30.993 回答
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];
}
于 2013-08-19T20:54:21.533 回答
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;
于 2013-08-19T21:13:44.737 回答