Ios UICollectionView中重复使用的单元格显示多个UIImageView,而它们只应显示一个

Ios UICollectionView中重复使用的单元格显示多个UIImageView,而它们只应显示一个,ios,objective-c,uiimage,uicollectionview,Ios,Objective C,Uiimage,Uicollectionview,我的UICollectionView有问题。最初,它显示精细,显示单元格网格,每个单元格都有一个UIImageView。这些uiImageView显示了存储在应用程序包中的透明PNG 我的问题是,一旦滚动了UICollectionView,一些单元格似乎已损坏 损坏的单元格显示多个堆叠在一起的图像,最上面的图像是它应该显示的图像,下面的图像是应该在其他单元格中使用的图像 我最好的猜测是,这与UICollectionView中的单元格的重用方式有关,但我愿意接受建议 这是我用于在UICollect

我的
UICollectionView
有问题。最初,它显示精细,显示单元格网格,每个单元格都有一个
UIImageView
。这些
uiImageView
显示了存储在应用程序包中的透明PNG

我的问题是,一旦滚动了
UICollectionView
,一些单元格似乎已损坏

损坏的单元格显示多个堆叠在一起的图像,最上面的图像是它应该显示的图像,下面的图像是应该在其他单元格中使用的图像

我最好的猜测是,这与
UICollectionView
中的单元格的重用方式有关,但我愿意接受建议

这是我用于在
UICollectionView
中创建单元格的委托代码:

// creates the individual cells to go in the menu view
- (UICollectionViewCell*) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    // create collection view cell
    UICollectionViewCell * cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];

    // create a uiview where we can place all views that need to go into this cell
    UIView * contents=[[UIView alloc] initWithFrame:cell.contentView.bounds];
    [contents setBackgroundColor:[UIColor clearColor]];
    [cell.contentView addSubview:contents];

    // add a button image
    NSString * buttonPath=[[NSBundle mainBundle] pathForResource:@"button" ofType:@"png" inDirectory:[[buttons objectAtIndex:indexPath.row] objectForKey:@"name"]];
    UIImage * button=[UIImage imageWithContentsOfFile:buttonPath];
    UIImageView * buttonView=[[UIImageView alloc] initWithImage:button];
    [buttonView setContentMode:UIViewContentModeScaleAspectFit];
    [buttonView setFrame:contents.bounds];
    [contents addSubview:buttonView];

    // set tag to the indexPath.row so we can access it later
    [cell setTag:indexPath.row];

    // add interactivity
    UITapGestureRecognizer * tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onButtonTapped:)];
    [tap setNumberOfTapsRequired:1];
    [cell addGestureRecognizer:tap];

    // return the cell
    return cell;

}
如果需要,我可以提供更多的代码


如何阻止单元格损坏?

问题是,您不断向
UICollectionViewCell
添加视图,因为它们被
UICollectionView
自动重用。因此,当调用
cellForItemAtIndexPath:
时,旧的
UIImageView
仍在单元格中,因为您正在添加一个

不要使用
addSubview:

相反,您可以创建一个自定义单元格,其中包含您想要的所有视图。因此,当调用
cellForItemAtIndexPath:
时,您只需设置此CustomCollectionViewCell的内容即可

这样它肯定会停止被破坏


如何构建自定义单元格

步骤1:创建.h和.m类

CustomCell.h

#import <UIKit/UIKit.h>

@interface CustomCell : UICollectionViewCell
{
    UIImageView *imageView;
}

@property (nonatomic, retain) UIImageView *imageView; //this imageview is the only thing we need right now.

@end
步骤2:导入它! 现在我们已经创建了CustomCell,我们可以将它导入到我们想要使用它的类中

步骤3:在行动中使用它

// creates the individual cells to go in the menu view
- (CustomCell*) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    // create collection view cell
    CustomCell *cell = (CustomCell *)[collectionView dequeueReusableCellWithReuseIdentifier:@"CustomCell" forIndexPath:indexPath]; //this is the place where the CustomCell does his magic.
    //Make sure to use the CustomCellReuseId that you register in the viewdidload/loadview (step4)

    // add a button image
    NSString * buttonPath=[[NSBundle mainBundle] pathForResource:@"button" ofType:@"png" inDirectory:[[buttons objectAtIndex:indexPath.row] objectForKey:@"name"]];

    cell.imageView.image = [UIImage imageWithContentsOfFile:buttonPath]; //place the image on the CustemCell.imageView as we prepared.

    // set tag to the indexPath.row so we can access it later
    [cell setTag:indexPath.row]; //we don't need this to access the cell but I left this in for your personal want.

/*
 * we can now do this from the CustomCell as well!
 *
    // add interactivity
    UITapGestureRecognizer * tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onButtonTapped:)];
    [tap setNumberOfTapsRequired:1];
    [cell addGestureRecognizer:tap];
*/
    // return the cell
    return cell;

}
步骤4:将单元格注册到collectionView

在viewDidLoad/loadView中添加以下行:

[_collectionView registerClass:[CustomCell class] forCellWithReuseIdentifier:@"CustomCell"];
步骤5:尽情享受吧!
您的自定义单元格已完成。现在做你想做的事,别忘了也喝点咖啡。

这是因为你每次在你的单元格中添加一个
UIImageView
来解决这个问题,你必须制作一个自定义单元格,然后像take一样使用它:

自定义.h

#import <UIKit/UIKit.h>

@interface CustomCell : UICollectionViewCell

@property (weak, nonatomic) IBOutlet UIImageView *imageView;

@end
您的控制器

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

    CustomCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"CustomCell" forIndexPath:indexPath];

    NSString * buttonPath=[[NSBundle mainBundle] pathForResource:@"button" ofType:@"png" inDirectory:[[buttons objectAtIndex:indexPath.row] objectForKey:@"name"]];
    UIImage * button=[UIImage imageWithContentsOfFile:buttonPath];

    [cell.imageView setImage:button];

    return cell;
}
您还必须将类似“CustomCell”的标识符设置为IB中的单元格

UICollectionViewCell * cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
加上这一行,

[[[cell contentView] subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];

对于以编程方式添加UICollectionView并具有自定义单元格(换句话说,没有XIB文件)的任何人,您必须将此行添加到viewDidLoad

[_collectionView registerClass:[CustomCell class] forCellWithReuseIdentifier:@"cellIdentifier"];

用于从uicollectionviewcell中的标签中删除重复文本

// Viewdidload
[_collectionView registerClass:[UICollectionviewcell class]     forCellWithReuseIdentifier:@"cellIdentifier"];



//in this method create label like this
-(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 *nameLbl=[[UILabel alloc] initWithFrame:CGRectMake(0, 10, 50, 20)];
     nameLbl.text=[Array objectAtIndex:indexpath.row];                                                                                                                                  
     nameLbl.textColor=[UIColor whiteColor];
     [cell.contentView addSubview:nameLbl];
     return cell;

   }

对于那些正在寻找快速答案的人,请将此函数添加到您的
CustomCell
类中:

override func prepareForReuse() {
    contentView.subviews.forEach({ $0.removeFromSuperview() })
    // replace contentView with the superview of the repeating content.
}

你是对的,这正是你重复使用细胞的方式。因为您只是使用addSubview:calls在单元格上一层接一层地添加。您能演示一下如何实现CustomerCollectionViewCell吗?当然可以。你想在事先知道故事板或纯代码的情况下完成吗?我也讨厌在故事板中这样做:P无论如何,这应该是纯代码中的。如果一切都清楚,请告诉我。啊,你需要继续投下牢房。^^现在请稍等。可能还应该生成返回值(CustomCell*)。我在这里,直到这是正确的,所以我得到你需要的答案!只要不在cellForItemAtIndexPath:函数中执行此操作。CustomCell完全可以为您自己的设计定制。此“自定义”单元格及其内部的所有内容正被系统正确地重新使用。这是删除它们的方法,但不应生成它们,因此您无需删除它们。当上一个集合视图单元格内容具有子视图,并且您希望删除该子视图并添加新的子视图时,此功能非常有用。当您将其设置为图像视图时,会出现这种情况。请描述您发布的内容!原因是什么?此外,它也不起作用:(这对我来说非常有效。对于初学者来说,这意味着您必须将覆盖功能添加到您连接插座的swift文件中。请参见
class DeviceCollectionViewCell:UICollectionViewCell{@IBOutlet弱var设备名称:UILabel!@IBOutlet弱var功能:UILabel!@IBOutlet弱var品牌标签:UILabel!@IBOutlet弱var SVGIconview:UIView!重写func prepareForReuse(){SVGIconview.subviews.forEach({$0.removeFromSuperview()}}
你救了我一天!非常感谢!
[_collectionView registerClass:[CustomCell class] forCellWithReuseIdentifier:@"cellIdentifier"];
// Viewdidload
[_collectionView registerClass:[UICollectionviewcell class]     forCellWithReuseIdentifier:@"cellIdentifier"];



//in this method create label like this
-(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 *nameLbl=[[UILabel alloc] initWithFrame:CGRectMake(0, 10, 50, 20)];
     nameLbl.text=[Array objectAtIndex:indexpath.row];                                                                                                                                  
     nameLbl.textColor=[UIColor whiteColor];
     [cell.contentView addSubview:nameLbl];
     return cell;

   }
override func prepareForReuse() {
    contentView.subviews.forEach({ $0.removeFromSuperview() })
    // replace contentView with the superview of the repeating content.
}