ios UICollectionView 中重用的单元格显示多个 UIImageViews,而它们应该只显示一个

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19269266/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 02:14:15  来源:igfitidea点击:

Reused cells in a UICollectionView show multiple UIImageViews when they should only show one

iosobjective-cuiimageuicollectionview

提问by Jimmery

Im having a problem with my UICollectionView. Initially it displays fine, showing a grid of cells, each cell with a single UIImageView. These UIImageViewsare showing PNGs with transparency that are stored in the app's bundle.

我的UICollectionView. 最初它显示良好,显示一个单元格网格,每个单元格都有一个UIImageView. 这些UIImageViews显示了存储在应用程序包中的具有透明度的 PNG。

My problem is, once the UICollectionViewhas been scrolled, some of the cells seem to be corrupt.

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

A corrupt cell shows multiple images stacked on top of each other, the top most image is the one it should be showing, and images underneath are the ones that should be used in other cells.

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

My best guess is that this has something to do with the way cells inside a UICollectionVieware reused, but I am open to suggestions.

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

This is the delegate code I use for creating cells within the 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;

}

I can provide more code if required.

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

How can I stop the cells from corrupting?

我怎样才能阻止细胞损坏?

回答by Totumus Maximus

The problem is that you keep adding views to the UICollectionViewCellas they are being reused automagically by the UICollectionView. So the old UIImageView's are still on the cell as you are adding one more as the cellForItemAtIndexPath:is called.

问题是您不断向 中添加视图,UICollectionViewCell因为UICollectionView. 所以旧UIImageView的仍然在单元格上,因为你正在添加一个cellForItemAtIndexPath:被调用的。

DO NOT USE addSubview:!

不要使用addSubview:

Instead you could make a custom cell with all the views you want already in them. So that when the cellForItemAtIndexPath:is called you only need to set the contents of this CustomCollectionViewCell instead.

相反,您可以制作一个自定义单元格,其中包含您想要的所有视图。这样当cellForItemAtIndexPath:被调用时,您只需要设置这个 CustomCollectionViewCell 的内容。

This way it will certainly stop being corrupted.

这样它肯定会停止被破坏。



How to build a CustomCell.

如何构建 CustomCell。

Step1: Create the .h & .m class.

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

CustomCell.h

自定义单元.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

CustomCell.m

自定义单元.m

#import "CustomCell.h"

@implementation CustomCell

@synthesize imageView;

- (id)initWithFrame:(CGRect)aRect
{
    if (self = [super initWithFrame:aRect])
    {
         //we create the UIImageView in this overwritten init so that we always have it at hand.
         imageView = [UIImageView alloc] init];
         //set specs and special wants for the imageView here.
         [self addSubview:imageView]; //the only place we want to do this addSubview: is here!

         //You wanted the imageView to react to touches and gestures. We can do that here too.
         UITapGestureRecognizer * tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onButtonTapped:)];
        [tap setNumberOfTapsRequired:1];
        [self addGestureRecognizer:tap];


        //We can also prepare views with additional contents here!
        //just add more labels/views/whatever you want.
    }
    return self;
}

-(void)onButtonTapped:(id)sender
{
    //the response to the gesture.
    //mind that this is done in the cell. If you don't want things to happen from this cell.
    //then you can still activate this the way you did in your question.

}

Step2: Import it! Now that we created the CustomCell we can import it in the class we want to use it in.

第二步:导入!现在我们创建了 CustomCell,我们可以将它导入到我们想要使用它的类中。

Step3: Use it in action!

第三步:在行动中使用它!

// 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;

}

Step4: Register the cell to the collectionView

Step4:将cell注册到collectionView

in the viewDidLoad / loadView add this line:

在 viewDidLoad / loadView 添加这一行:

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

Step5: Enjoy! Your CustomCell is done. Now do whatever you like and don't forget to get some coffee too.

第5步:享受!您的 CustomCell 已完成。现在做你喜欢做的事,不要忘记喝咖啡。

回答by itsji10dra

Right after

紧随其后

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

Just add this line,

只需添加这一行,

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

回答by Mirko Catalano

That Happen because you add every time a UIImageViewto your cell for fix this problem you have to Make a custom cell and then use it like take:

发生这种情况是因为您每次都将 a 添加UIImageView到您的单元格以解决此问题,您必须制作一个自定义单元格,然后像使用一样使用它:

Custom.h

自定义.h

#import <UIKit/UIKit.h>

@interface CustomCell : UICollectionViewCell

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

@end

Custom.m

自定义.m

#import "CustomCell.h"

@implementation CustomCell

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Drawing code
}
*/

@end

Your Controller

您的控制器

- (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;
}

You have also to set 'CustomCell' like Identifier to the Cell in IB

您还必须将 'CustomCell' 之类的标识符设置为 IB 中的单元格

回答by Bright Future

For those who are looking for a Swifty answer, add this function to your CustomCellclass:

对于那些正在寻找 Swifty 答案的人,请将此功能添加到您的CustomCell课程中:

override func prepareForReuse() {
    contentView.subviews.forEach({ 
// 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;

   }
.removeFromSuperview() }) // replace contentView with the superview of the repeating content. }

回答by ShigaSuresh

This is for removing duplicate text from label in uicollectionviewcell.

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

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

回答by Mayank Verma

For anyone who is adding UICollectionView programmatically and have a custom cell, in other words no XIB file, then you have to add this line to viewDidLoad

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

for (UIView *prevSubview in cell.contentView.subviews) {
    [prevSubview removeFromSuperview];
}

回答by vp_gold

##代码##