Las celdas reutilizadas en un UICollectionView muestran múltiples UIImageViews cuando solo deberían mostrar una


Estoy teniendo un problema con mi UICollectionView. Inicialmente se muestra bien, mostrando una cuadrícula de celdas, cada celda con un solo UIImageView. Estos UIImageViews están mostrando PNGS con transparencia que se almacenan en el paquete de la aplicación.

Mi problema es que, una vez que el UICollectionView ha sido desplazado, algunas de las celdas parecen estar corruptas.

Una celda corrupta muestra varias imágenes apiladas una encima de la otra, la imagen más alta es la que debe mostrar, y las imágenes debajo son las que deben usarse en otras celdas.

Mi mejor conjetura es que esto tiene algo que ver con la forma en que las celdas dentro de un UICollectionView se reutilizan, pero estoy abierto a sugerencias.

Este es el código delegado que uso para crear celdas dentro de 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;

}

Puedo proporcionar más código si es necesario.

¿Cómo puedo evitar que las células se corrompan?

Author: Mirko Catalano, 2013-10-09

7 answers

El problema es que sigues agregando vistas a UICollectionViewCell ya que están siendo reutilizadas automáticamente por UICollectionView. Así que los viejos UIImageView's todavía están en la celda como usted está agregando uno más como el cellForItemAtIndexPath: se llama.

NO USE addSubview:!

En su lugar, podría crear una celda personalizada con todas las vistas que desee ya en ellas. De modo que cuando se llama a cellForItemAtIndexPath: solo necesita establecer el contenido de este CustomCollectionViewCell en su lugar.

De esta manera, sin duda dejará de ser dañado.


Cómo construir una célula personalizada.

Step1 : Crear el .h&.clase 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

CustomCell.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 : ¡Impórtalo! Ahora que hemos creado el CustomCell podemos importarlo en la clase en la que queremos usarlo.

Step3: ¡Úsalo en acción!

// 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 : Registre la celda en collectionView

En viewDidLoad / loadView agregue esto línea:

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

Step5 : ¡Disfruta! Tu CustomCell está hecho. Ahora haz lo que quieras y no olvides tomar un poco de café también.

 42
Author: Totumus Maximus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-05 09:21:11

Justo después de

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

Simplemente agregue esta línea,

[[[cell contentView] subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
 6
Author: itsji10dra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-06 08:29:42

Eso sucede porque agregas cada vez un UIImageView a tu celda para solucionar este problema tienes que hacer una celda personalizada y luego usarla como take:

Personalizado.h

#import <UIKit/UIKit.h>

@interface CustomCell : UICollectionViewCell

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

@end

Personalizado.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

Su Controlador

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

También debe establecer un Identificador similar a' CustomCell ' en la celda en IB

 4
Author: Mirko Catalano,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-05 05:43:42

Para aquellos que buscan una respuesta rápida, agregue esta función a su clase CustomCell:

override func prepareForReuse() {
    contentView.subviews.forEach({ $0.removeFromSuperview() })
    // replace contentView with the superview of the repeating content.
}
 2
Author: Bright Future,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-25 07:29:13

Para cualquiera que esté agregando UICollectionView programáticamente y tenga una celda personalizada, en otras palabras, sin archivo XIB, entonces debe agregar esta línea para viewDidLoad

[_collectionView registerClass:[CustomCell class] forCellWithReuseIdentifier:@"cellIdentifier"];
 0
Author: Mayank Verma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-27 19:42:32
This is for removing duplicate text from label in 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;

   }
 0
Author: ShigaSuresh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-20 10:10:16
for (UIView *prevSubview in cell.contentView.subviews) {
    [prevSubview removeFromSuperview];
}
 -1
Author: vp_gold,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-25 23:52:20