UICollectionView 上的可重用性问题

Posted

技术标签:

【中文标题】UICollectionView 上的可重用性问题【英文标题】:Reusability issue on UICollectionView 【发布时间】:2014-03-27 05:15:06 【问题描述】:

我曾使用过UITableView,但我从未在我的应用程序中使用过UICollectionView。所以我想以编程方式创建UICollectionView

以下是我的代码:

UICollectionViewFlowLayout *layout =[[UICollectionViewFlowLayout alloc] init];
    _collectionView=[[UICollectionView alloc] initWithFrame:CGRectMake(0, 43, self.view.frame.size.width, self.view.frame.size.height - 84) collectionViewLayout:layout];
    [_collectionView setDataSource:self];
    [_collectionView setDelegate:self];
    [_collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cellIdentifier"];
    layout.sectionInset = UIEdgeInsetsMake(5, 5, 5, 5);
    layout.minimumInteritemSpacing = 5;
    [_collectionView setBackgroundColor:self.view.backgroundColor];        
    [self.view addSubview:_collectionView];

委托和数据源方法。

#pragma mark -
#pragma mark - UITableView Delegate Methods

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

    return 15;


- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];

    if (cell.selected)
        cell.backgroundColor = [UIColor lightGrayColor]; // highlight selection cell
    else
        cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background-grid.png"]]; // Default Cell

    UIImageView *imgPhoto = [[UIImageView alloc] init];
    imgPhoto.userInteractionEnabled = YES;
    imgPhoto.backgroundColor = [UIColor grayColor];
    imgPhoto.frame =  CGRectMake(3.5, 5, 90, 80);
    imgPhoto.clipsToBounds = YES;
    imgPhoto.image = [UIImage imageNamed:@"product.png"];
    [cell.contentView addSubview:imgPhoto];

    UILabel *lblCategoryTitle = [[UILabel alloc] init];
    [lblCategoryTitle  setFont: [UIFont fontWithName:@"OpenSans-Bold" size:14]];
    lblCategoryTitle.textAlignment = NSTextAlignmentCenter;
    lblCategoryTitle.frame = CGRectMake(3.5, 90, 90, 24);
    lblCategoryTitle.textColor = [UIColor blackColor];
    lblCategoryTitle.text = @"Product 1"; 
    lblCategoryTitle.backgroundColor = [UIColor clearColor];
    lblCategoryTitle.numberOfLines = 2;
    [cell.contentView addSubview:lblCategoryTitle];

    return cell;


- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath

    return CGSizeMake(97, 118);


-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath  

    UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath];
    datasetCell.backgroundColor = [UIColor lightGrayColor]; // highlight selection


-(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath 

    UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath];
    datasetCell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background-grid.png"]]; // default cell

然后我的屏幕看起来像

问题 1 -查看上面的屏幕,您会看到第 1 和第 3 项看起来很模糊(请参阅 产品 1 然后是第 2 项/中间项目?为什么会这样?

每当我向上/向下滚动UICollectionView 时,项目都会被覆盖,请查看下一张图片

看了这张图片后,根据我对UITableView 的经验,这是因为UICollectionView 的单元格的可重用性而发生的。

问题 2 - 那我该如何解决呢?

请给我你的建议并帮助我解决这个问题。

已编辑: @Dima 的建议)

自定义单元格

.h 文件

#import <UIKit/UIKit.h>

@interface customeGridCell : UICollectionViewCell

@property (nonatomic, strong) UIImageView *imgPhoto;
@property (nonatomic, strong) UILabel *lblCategoryTitle;

@end

.m 文件

#import "customeGridCell.h"

@implementation customeGridCell

- (id)initWithFrame:(CGRect)frame

    self = [super initWithFrame:frame];
    if (self)
    
        self.imgPhoto = [[UIImageView alloc] init];
        self.imgPhoto.userInteractionEnabled = YES;
        self.imgPhoto.backgroundColor = [UIColor grayColor];
        self.imgPhoto.frame =  CGRectMake(3.5, 5, 90, 80);
        [self addSubview:self.imgPhoto];

        self.lblCategoryTitle = [[UILabel alloc] init];
        [self.lblCategoryTitle  setFont: [UIFont fontWithName:@"OpenSans-Bold" size:14]];
        self.lblCategoryTitle.textAlignment = NSTextAlignmentCenter;
        self.lblCategoryTitle.frame = CGRectMake(3.5, 90, 90, 24);
        self.lblCategoryTitle.textColor = [UIColor blackColor];
        self.lblCategoryTitle.backgroundColor = [UIColor clearColor];
        self.lblCategoryTitle.numberOfLines = 2;
        [self addSubview:self.lblCategoryTitle];
    
    return self;

以及cellForItemAtIndexPath的代码

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

    customeGridCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];

    if (cell.selected)
        cell.backgroundColor = [UIColor lightGrayColor]; // highlight selection cell
    else
        cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background-grid.png"]]; // Default Cell


    cell.imgPhoto.image =  [UIImage imageNamed:@"product.png"];
    cell.lblCategoryTitle.text = @"Product 1";

    return cell;

【问题讨论】:

用我的方法改变你的方法...你的问题会解决的 【参考方案1】:

问题出在您的collectionView:cellForItemAtIndexPath: 方法中。每次重用单元格时,您都在添加这些子视图,彼此叠加。

您应该创建一个UICollectionViewCell 子类并将所有您想要的额外子视图添加到它的初始化程序中。这将确保它们只被添加一次。

示例代码:

这是一个示例,说明如何将UICollectionViewCell 子类化

@interface MyCustomCell : UICollectionViewCell

@property (nonatomic, strong) UILabel *customLabel;
@property (nonatomic, strong) UIImageView *customImageView;
@end


// in implementation file
- (id)initWithFrame:(CGRect)frame

    self = [super initWithFrame:frame];
    if (self) 
    
        // initialize label and imageview here, then add them as subviews to the content view
    
    return self;

然后,当您抓取一个单元格时,您只需执行以下操作:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

    MyCustomCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];

    if (cell.selected)
        cell.backgroundColor = [UIColor lightGrayColor]; // highlight selection cell
    else
        cell.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background-grid.png"]]; // Default Cell

    cell.customImageView.image = // whatever
    cell.customLabel.text = // whatever

    return cell;

【讨论】:

感谢您的重播。您能用一些代码详细说明一下吗? 谢谢,我会试试的。 :) 它给了我诸如-[UICollectionViewCell customImageView]: unrecognized selector sent to instance之类的错误 你需要像我的代码一样创建属性,并在正确的地方初始化它。 衷心感谢您的朋友花费您的黄金时间来解决我的问题并提高我对UICollectionView的了解。再次感谢:)【参考方案2】:

你可以通过两种方式做到这一点。

删除 UILabel 表单视图。

 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];
    for (UILabel *lbl in cell.contentView.subviews)
    
        if ([lbl isKindOfClass:[UILabel class]])
        
            [lbl removeFromSuperview];
        
    
    UILabel *lblCategoryTitle =[[UILabel alloc]init];

    [lblCategoryTitle  setFont: [UIFont fontWithName:@"OpenSans-Bold" size:14]];
    lblCategoryTitle.textAlignment = NSTextAlignmentCenter;
    lblCategoryTitle.frame = CGRectMake(3.5, 90, 90, 24);
    lblCategoryTitle.textColor = [UIColor blackColor];
    lblCategoryTitle.text = @"Product 1";
    lblCategoryTitle.backgroundColor = [UIColor clearColor];
    lblCategoryTitle.numberOfLines = 2;
    [cell.contentView addSubview:lblCategoryTitle];
    return cell;

使用标签获取标签

 - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath];
    UILabel *lblCategoryTitle =(UILabel *) [cell viewWithTag:5];
    if (!lblCategoryTitle) 
        lblCategoryTitle=[[UILabel alloc]init];
        [cell.contentView addSubview:lblCategoryTitle];

    
    [lblCategoryTitle  setFont: [UIFont fontWithName:@"OpenSans-Bold" size:14]];
    lblCategoryTitle.tag=5;
    lblCategoryTitle.textAlignment = NSTextAlignmentCenter;
    lblCategoryTitle.frame = CGRectMake(3.5, 90, 90, 24);
    lblCategoryTitle.textColor = [UIColor blackColor];
    lblCategoryTitle.text = @"Product 1";
    lblCategoryTitle.backgroundColor = [UIColor clearColor];
    lblCategoryTitle.numberOfLines = 2;
    return cell;

【讨论】:

如何使用标签来做到这一点?我需要在哪里初始化 label 和 imageView ?? 你不需要每次都初始化标签,使用标签属性获取标签。 但是我需要在哪里声明标签,所以我使用它的标签? if (!lblCategoryTitle) 将检查您的标签是否已初始化,如果它不在将分配和初始化的标签之下。在此之后它将不再进行初始化。将从单元格中获取

以上是关于UICollectionView 上的可重用性问题的主要内容,如果未能解决你的问题,请参考以下文章

制作 iOS Gallery - UICollectionView 可重用性

List 中的可重用性支持 - SwiftUI

我们能否增加这种面向密钥的访问保护模式的可重用性?

如何停止 UIPickerView 的可重用性

iOS 中 AudioUnit Graph 的可重用性

不同应用状态结构级别的reducer的可重用性