web-dev-qa-db-fra.com

Appel de setCollectionViewLayout: l'animation ne recharge pas UICollectionView

J'utilise UICollectionView avec deux mises en page personnalisées - qui sont sous-classées à partir de UICollectionViewFlowLayout

Lorsque mon affichage est chargé pour la première fois, je le configure comme affichage par défaut en utilisant: 

[self.collectionView setCollectionViewLayout:self.tableViewLayout];

Cela appelle: cellForItemAtIndexPath - donc pas de problème là-bas. C'est dans la méthode viewWillAppear.

Ensuite, j'utilise un UIButton pour changer la disposition de la vue actuelle à la suivante, comme suit: 

-(void)changeViewLayoutButtonPressed
{
    self.changeLayout = !self.changeLayout;


    if (self.changeLayout){

        [self.tradeFeedCollectionView setCollectionViewLayout:self.grideLayout animated:YES];

    }

    else {


        [self.tradeFeedCollectionView setCollectionViewLayout:self.tableViewLayout animated:YES];

    }
}

Après cet appel, numberOfItemsInSection est appelé. J'ai vérifié dans le débogueur que le compte de retour n'est pas nul. 

Après cet appel, rien d’autre n’est appelé et mon UICollectionView change la disposition mais cellForItemAtIndexPath n’est pas appelé - les cellules ne sont pas configurées correctement.

si je mets [self.collectionView roloadData] à l'intérieur de numberOfItemsInSection - alors cellForItemAtIndexPath est appelé et les cellules sont configurées. 

Je ne devrais pas avoir besoin de faire un appel manuel pour recharger les données dans numberOfItemsInSection. Quelqu'un peut-il me dire ce qui se passe?

Modifier

J'ai oublié de mentionner que j'utilise deux UINibs différents pour les deux cellules car ils ont besoin de dispositions légèrement différentes. Serait-ce un problème? Merci!

Modifier 2

D'accord, j'ai donc réussi à le faire fonctionner presque comme je le voulais. Voici tout le code utilisé pour la vue Collection et la façon dont je modifie ses dispositions. 

Les mises en page personnalisées

Celles-ci sont sous-classées à partir de UICollectionViewFlowLayout - La raison pour laquelle j'ai sous-classé est simplement due au visage que ma UICollectionView doit ressembler et se comporter de manière très proche de la mise en page prédéfinie d'Apple. Juste différentes tailles de cellules, cependant. 

TableViewLayout - Aucun code dans le fichier d'en-tête. - BBTradeFeedTableViewLayout.m

@implementation BBTradeFeedTableViewLayout

-(id)init
{
    self = [super init];

    if (self){

        self.itemSize = CGSizeMake(320, 80);
        self.minimumLineSpacing = 0.1f;
    }

    return self;
}
@end

Gride Layout - Aucun code dans le fichier d’en-tête. - BBTradeFeedGridViewLayout.m

   @implementation BBTradeFeedGridViewLayout

-(id)init
{
    self = [super init];

    if (self){

        self.itemSize = CGSizeMake(159, 200);
        self.minimumInteritemSpacing = 0.1f;
        self.minimumLineSpacing = 0.1f;
    }
    return self; 
}
@end

Puis dans la ViewController qui a ma UICollectionView - je déclare les deux dispositions personnalisées comme suit: 

@property (strong, nonatomic) BBTradeFeedTableViewLayout *tableViewLayout;
@property (strong, nonatomic) BBTradeFeedGridViewLayout *grideLayout;

J'enregistre ensuite les deux fichiers .xib pour la collectionView:

  [self.tradeFeedCollectionView registerNib:[UINib nibWithNibName:@"BBItemTableViewCell" bundle:nil] forCellWithReuseIdentifier:@"TableItemCell"];
        [self.tradeFeedCollectionView registerNib:[UINib nibWithNibName:@"BBItemGridViewCell" bundle:nil] forCellWithReuseIdentifier:@"GridItemCell"];

Ensuite, j'ai un UIButton qui change la disposition: 

    -(void)changeViewLayoutButtonPressed
{

    if (!self.gridLayoutActive){

        self.gridLayoutActive = YES;
        [self.tradeFeedCollectionView setCollectionViewLayout:self.grideLayout animated:YES];
        self.lastLayoutUsed = @"GridLayout";

    }

    else {

        self.gridLayoutActive = NO;

        [self.tradeFeedCollectionView setCollectionViewLayout:self.tableViewLayout animated:YES];
        self.lastLayoutUsed = @"TableLayOut";
    }
    [self.tradeFeedCollectionView reloadItemsAtIndexPaths:[self.tradeFeedCollectionView indexPathsForVisibleItems]];

}

Enfin - lorsqu'un appel de service Web est effectué - il appelle [self.tradeFeedCollectionView reloadData];

Donc là ma UICollectionView est rechargée et la dernière méthode: 

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

{   static NSString *tableCellIdentifier = @"TableItemCell";
    static NSString *gridCellIdentifier = @"GridItemCell";

    if (indexPath.item < [self.tradeSearchArray count]){

    if (self.gridLayoutActive == NO){

        BBItemTableViewCell *tableItemCell = [collectionView dequeueReusableCellWithReuseIdentifier:tableCellIdentifier forIndexPath:indexPath];

    if ([self.tradeSearchArray count] > 0){

        self.toolBarButtomItem.title = [NSString stringWithFormat:@"%d Results", self.searchResult.searchResults];

        tableItemCell.gridView = NO;
        tableItemCell.backgroundColor = [UIColor whiteColor];
        tableItemCell.item = self.tradeSearchArray[indexPath.row];

    }
        return tableItemCell;
}else

    {

        BBItemTableViewCell *gridItemCell= [collectionView dequeueReusableCellWithReuseIdentifier:gridCellIdentifier forIndexPath:indexPath];

    if ([self.tradeSearchArray count] > 0){

        self.toolBarButtomItem.title = [NSString stringWithFormat:@"%d Results", self.searchResult.searchResults];


        gridItemCell.gridView = YES;
        gridItemCell.backgroundColor = [UIColor whiteColor];
        gridItemCell.item = self.tradeSearchArray[indexPath.row];

    }

    return gridItemCell;
    }

Le problème avec le code ci-dessus

Le code ci-dessus fonctionne. Cependant, les cellules ne s'animent pas bien et cela semble étrange, presque comme si un rafraîchissement avait eu lieu. Ce qui est dû à l'appel:

[self.tradeFeedCollectionView reloadItemsAtIndexPaths:[self.tradeFeedCollectionView indexPathsForVisibleItems]];

Mais c’est la seule façon pour moi d’être proche de ce que je veux. 

19
Robert J. Clegg

-setCollectionViewLayout: ou -setCollectionViewLayout:animated: ne provoquera pas le rechargement de votre UICollectionView

  • Si vous souhaitez modifier votre style de cellule ou mettre à jour les données, appelez les méthodes de protocole [self.collectionView reload] et UICollectionViewDataSource
  • Si vous souhaitez modifier votre présentation UICollectionView en une autre, appelez -setCollectionViewLayout:. Ou si vous souhaitez mettre à jour votre structure UICollectionView, appelez simplement [self.collectionView.collectionViewLayout invalidateLayout]

La disposition des cellules et les données sont deux choses différentes dans UICollectionView.

Mettre à jour
vous devriez jeter toutes les données périmées, probablement par -reloadData. Et calculez les nouvelles images pour chaque cellule de vos sous-classes UICollectionViewLayout. Remplacer - (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath et - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect.
Vous décidez de la nouvelle contentOffset. Par exemple, vous pouvez conserver le chemin indexPath d’une cellule visible et décider de faire défiler jusqu’à cette indexPath ou setContentOffset: au frame.Origin de la nouvelle cellule à la même indexPath après le changement de présentation. 

20
liuyaodong

Appelez simplement reloadData avant vous changez de présentation (l’appel performBatchUpdates n’est pas obligatoire):

[self.collectionView reloadData];
[self.collectionView.collectionViewLayout invalidateLayout];
[self.collectionView setCollectionViewLayout:yourLayout animated:YES];

En rapide:

self.collectionView?.reloadData()
self.collectionView?.collectionViewLayout.invalidateLayout()
self.collectionView?.setCollectionViewLayout(yourLayout, animated: true)
16
Artheyn

Vous devriez appeler [self.tradeFeedCollectionView invalidateLayout]; avant 

if (self.changeLayout){
    [self.tradeFeedCollectionView setCollectionViewLayout:self.grideLayout animated:YES];
}
else {
    [self.tradeFeedCollectionView setCollectionViewLayout:self.tableViewLayout animated:YES];
}
1
Lolloz89

(Solution enregistrée au nom de l'auteur de la question) .

J'ai finalement trouvé une solution simple et agréable. 

La réponse marquée était correcte, que je devais appeler, -reloadData sur ma UICollectionView - Cependant, cela détruisait l'animation lors du basculement entre les deux cellules. 

Après avoir pris en compte les suggestions de la réponse marquée - je viens de retravailler ma méthode `UIButton ', cela change les cellules

J'ai trouvé que si j'appelle: 

[self.collectionview performBatchUpdates:^{  } completion:^(BOOL finished) {

}];

Selon la vidéo WDDC - cela anime les changements fonctionne avec la méthode -reloadData

Exemple: 

[self.collectionView reloadData]; 
    [self.collectionView performBatchUpdates:^{
        [self.collectionView.collectionViewLayout invalidateLayout];
        [self.collectionView setCollectionViewLayout:self.grideLayout animated:YES];
    } completion:^(BOOL finished) {

    }];
}
0
halfer

J'ai trouvé que pour animer facilement et agréablement le type et la disposition des deux cellules simultanément, il est préférable d'effectuer reloadSections (au lieu de reloadData), puis de définir une nouvelle disposition avec animation:

[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
[self.collectionView setCollectionViewLayout:newLayout animated:animated];
0
Rivera

Voici l'astuce si vous souhaitez mettre à jour des modèles

[self.collectionView removeFromSuperview]; //First remove the Collection View

//Relocate the view with layouts
UICollectionViewFlowLayout *layout=[[UICollectionViewFlowLayout alloc] init];
layout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
layout.minimumInteritemSpacing = 10;
layout.minimumLineSpacing = 10;

 self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.width,100) collectionViewLayout:layout];
[self.collectionView setDataSource:self];
[self.collectionView setDelegate:self];
[self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cellIdentifier"];
[self.collectionView reloadData];

[self.view addSubview:self.collectionView];
0
Shivam Singh

Vous devriez seulement avoir à appeler cette méthode:

[self.collectionView setCollectionViewLayout:layout animated:YES];

J'avais un bogue similaire, mais mes sous-vues des cellules n'étaient pas configurées avec la mise en page automatique. La cellule était correctement redimensionnée, mais mes sous-vues ne l'étaient pas. La taille de la cellule ne semblait donc jamais avoir changé. Vérifiez cela rapidement dans votre code en définissant clipsToBounds = YES sur votre cell.contentView.

0
Michael Rose