4
votes

L'en-tête collant UICollectionView disparaît pendant un certain temps après l'insertion de la section lorsque la collection est trop défilée (effet de rebond)

J'utilise UICollectionReusableView comme en-tête de la section UICollectionView . J'ai activé les "en-têtes collants" avec:

UIView.performWithoutAnimation {
    collectionView.performBatchUpdates({
        self.collectionView.insertSections(IndexSet(integersIn: collectionView.numberOfSections...viewModel.numberOfSections - 1))
    }, completion: nil)
}

J'insère de nouvelles sections dans la collection avec:

collectionView.performBatchUpdates({
    self.collectionView.insertSections(IndexSet(integersIn: collectionView.numberOfSections...viewModel.numberOfSections - 1))
}, completion: nil)

Si l'insertion se produit lors de la collecte est dépassé (le rebond est activé), l'en-tête disparaîtra pendant un certain temps (voir GIF ci-dessous). Comment éviter ce comportement?

J'utilise iOS 12.1.4, mais le même problème se produit également sur les simulateurs iOS 11.x et 12.x.

Le problème ne se produit pas si l'effet de rebond est désactivé, mais je souhaite le conserver pour une sensation de défilement plus fluide. J'ai essayé d'invalider la mise en page avant / après la mise à jour sans résultat. Merci pour vos conseils.

 entrez la description de l'image ici

MODIFIER (26/02/2019)
Solution de contournement: Enveloppant l'insertion dans performWithoutAnimation , l'en-tête de résolution de bloc disparaît, mais désactivez évidemment l'animation de rechargement.

let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout
layout?.sectionHeadersPinToVisibleBounds = true


0 commentaires

3 Réponses :


1
votes

Malheureusement, en appelant performBatchUpdates, la mise en page anime automatiquement tous les éléments elle-même. Même jusqu'à présent, il n'y a aucun moyen de dire explicitement quels éléments animer et lesquels ne pas animer.

Cependant, j'ai trouvé une résolution qui est une sorte d'anti-pattern.

Pour votre classe d'en-tête, remplacez ces méthodes:

-(void)prepareForCollectionViewUpdates:(NSArray<UICollectionViewUpdateItem *> *)updateItems
{
    // Keep track of insert and delete index paths
    [super prepareForCollectionViewUpdates:updateItems];
    
    [self.indexPaths2Delete removeAllObjects];
    [self.indexPaths2Insert removeAllObjects];
    
    for (UICollectionViewUpdateItem *update in updateItems)
    {
        if (update.updateAction == UICollectionUpdateActionDelete)
        {
            [self.indexPaths2Delete addObject:update.indexPathBeforeUpdate];
        }
        else if (update.updateAction == UICollectionUpdateActionInsert)
        {
            [self.indexPaths2Insert addObject:update.indexPathAfterUpdate];
        }
    }

    if (self.indexPaths2Insert.count > 0 || self.indexPaths2Delete.count > 0)
    {
        HomeHeaderView.liveInstance.shouldDisableAnimations = false; //since it may cause scrolling, we should enable the animations
    }
    else
        HomeHeaderView.liveInstance.shouldDisableAnimations = true; //there's nothing added or deleted, so keep the header sticked.
}

Maintenant, si shouldDisableAnimations est vrai, les animations automatiques de collectionView ne seront pas appliquées à notre en-tête.

Cependant, la désactivation des animations pour l'en-tête peut provoquer d'autres problèmes (par exemple lorsque vous faites défiler beaucoup plus bas, puis supprimez toutes les cellules. l'en-tête passera instantanément en haut et la collectionView défilera vers le haut avec une animation entraînant un problème!)

Pour gérer cela, nous devons définir shouldDisableAnimations pour le en-tête à l'heure correcte qui correspond à l'appel de prepareForCollectionViewUpdates. Malheureusement, nous ne pouvons pas faire cela via l'attribut de l'en-tête car les attributs sont appliqués à l'en-tête une fois l'animation terminée. Nous devons donc accéder directement à l'instance de l'en-tête directement à partir de la méthode prepareForCollectionViewUpdates. (il y a plusieurs façons de faire cela. J'ai fait cela en gardant une référence faible de l'en-tête sur une propriété de classe d'elle-même)

       -(void)setBounds:(CGRect)bounds
        {
            [CATransaction begin];
            [CATransaction setDisableActions:self.shouldDisableAnimations];
            [super setBounds:bounds];
            [CATransaction commit];
        }
    
        //-(void)setCenter:(CGPoint)center
    
    
        - (void)setCenter:(CGPoint)center
        {
            [CATransaction begin];
            [CATransaction setDisableActions:self.shouldDisableAnimations];
            [super setCenter:center];
            [CATransaction commit];
        }


0 commentaires

0
votes

La solution ci-dessus que j'ai fournie précédemment ne semble pas fonctionner sur iOS 13.

Pendant performBatchUpdates, UICollectionView n'applique pas layoutAttributes pour les éléments. La seule façon de résoudre ce problème est de définir explicitement le cadre (ou tout autre paramètre) sur l'en-tête à partir de la méthode prepare () de la mise en page. Comme une animation est en cours, UICollectionView ne sera pas mise en page pendant l'animation.

Je pense qu'Apple doit ajouter une fonctionnalité pour marquer explicitement quels éléments participent à l'animation et lesquels non. Cela peut être fait via les attributs layoutAttributes des éléments ou une méthode distincte dans UICollectionViewLayout.

L'accès aux instances d'élément depuis la mise en page est un non-non! Mais il n'y a pas d'autres solutions de contournement pour le moment.


0 commentaires

0
votes

J'avais aussi le même problème, mais le travail que j'ai fait est de recharger la collectionView (l'en-tête collant ne disparaît pas) elle-même au lieu de l'insérer.


0 commentaires