web-dev-qa-db-fra.com

Les cellules réutilisées dans UICollectionView affichent plusieurs UIImageViews lorsqu'elles ne doivent en afficher qu'un.

J'ai un problème avec ma UICollectionView. Initialement, l’affichage est correct et affiche une grille de cellules, chaque cellule n’ayant qu’une seule variable UIImageView. Ces UIImageViews affichent des PNG avec une transparence qui sont stockés dans le bundle de l'application. 

Mon problème est que, une fois que la variable UICollectionView a été déroulée, certaines cellules semblent corrompues.

Une cellule corrompue contient plusieurs images empilées les unes sur les autres, l'image la plus haute étant celle qu'elle devrait être affichée et les images situées en dessous sont celles qui doivent être utilisées dans les autres cellules.

Ma meilleure hypothèse est que cela a quelque chose à voir avec la façon dont les cellules à l'intérieur d'une UICollectionView sont réutilisées, mais je suis ouvert aux suggestions.

C'est le code de délégué que j'utilise pour créer des cellules dans la 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;

}

Je peux fournir plus de code si nécessaire.

Comment puis-je empêcher les cellules de se corrompre?

29
Jimmery

Le problème est que vous continuez d’ajouter des vues à la UICollectionViewCell car elles sont réutilisées automatiquement par la UICollectionView. Ainsi, les anciennes variables UIImageView sont toujours présentes dans la cellule, car vous en ajoutez une de plus lorsque le cellForItemAtIndexPath: est appelé.

NE PAS UTILISER addSubview:!

Au lieu de cela, vous pouvez créer une cellule personnalisée avec toutes les vues souhaitées. Ainsi, lorsque le cellForItemAtIndexPath: est appelé, il vous suffit de définir le contenu de ce CustomCollectionViewCell.

De cette façon, il ne sera certainement plus corrompu.


Comment construire un CustomCell.

Step1 : Créez la classe .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

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 : Importez-le! Maintenant que nous avons créé le CustomCell, nous pouvons l'importer dans la classe dans laquelle nous souhaitons l'utiliser.

Step3 : Utilisez-le en 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;

}

Étape 4 : Enregistrer la cellule dans la collection Voir

dans le viewDidLoad/loadView, ajoutez cette ligne:

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

Step5 : Enjoy! Votre CustomCell est terminé. Maintenant, fais ce que tu veux et n'oublie pas de prendre un café aussi.

42
Totumus Maximus

Juste après

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

Ajoutez juste cette ligne,

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

Cela se produit parce que vous ajoutez chaque fois une UIImageView à votre cellule pour résoudre ce problème, vous devez Créer une cellule personnalisée, puis l'utiliser comme suit

Custom.h

#import <UIKit/UIKit.h>

@interface CustomCell : UICollectionViewCell

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

@end

Personnalisé.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

Votre contrôleur

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

Vous devez également définir 'CustomCell' comme Identifier sur la cellule dans IB

4
Mirko Catalano

Pour ceux qui recherchent une réponse à Swifty, ajoutez cette fonction à votre classe CustomCell

override func prepareForReuse() {
    contentView.subviews.forEach({ $0.removeFromSuperview() })
    // replace contentView with the superview of the repeating content.
}
2
Bright Future
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
ShigaSuresh

Pour ceux qui ajoutent UICollectionView par programme et ont une cellule personnalisée, autrement dit, aucun fichier XIB, vous devez ajouter cette ligne pour viewDidLoad

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