web-dev-qa-db-fra.com

Recharger l'en-tête ou le pied de page UICollectionView?

Certaines données extraites dans un autre thread mettent à jour l'en-tête d'un UICollectionView. Cependant, je n'ai pas trouvé de moyen efficace de recharger une vue supplémentaire telle qu'un en-tête ou un pied de page. 

Je peux appeler collectionView reloadSections:, mais cela recharge toute la section, ce qui est inutile. collectionView reloadItemsAtIndexPaths: ne semble cibler que les cellules (pas les vues supplémentaires). Et appeler setNeedsDisplay sur l'en-tête lui-même ne semble pas fonctionner non plus. Est-ce que je manque quelque chose?

29
akaru

Vous pouvez également utiliser (la manière paresseuse)

collectionView.collectionViewLayout.invalidateLayout() // Swift

[[_collectionView collectionViewLayout] invalidateLayout] // objc

Plus complexe serait de fournir un contexte

collectionView.collectionViewLayout.invalidateLayout(with: context) // Swift

[[_collectionView collectionViewLayout] invalidateLayoutWithContext:context] // objc

Vous pouvez ensuite créer ou configurer vous-même le contexte pour indiquer ce qui doit être mis à jour voir: UICollectionViewLayoutInvalidationContext

Il possède une fonction que vous pouvez remplacer:

invalidateSupplementaryElements(ofKind:at:) // Swift
20
Saren Inden

Je viens de rencontrer le même problème, et j'ai fini par regarder la vue en utilisant sa balise pour éditer une étiquette:

UICollectionReusableView *footer = (UICollectionReusableView*)[self.collectionView viewWithTag:999];
UILabel *footerLabel = (UILabel*)[footer viewWithTag:100];

Comme vous l'avez dit, il n'est pas nécessaire de recharger une section entière, ce qui annule également toute animation sur les cellules. Ma solution n'est pas idéale, mais c'est assez simple.

11
Bob Vork

J'ai le même problème. J'ai essayé la réponse de @ BobVorks et cela fonctionne bien, si seulement la cellule était réutilisée, sinon elle ne le serait pas. J'ai donc essayé de trouver un moyen plus propre de réaliser cela et je suis venu en rechargeant tout le UICollectionView après le performBatchUpdate (bloc d'achèvement) et cela fonctionne très bien. Il recharge la collection sans aucune annulation d'animation dans insertItemsAtIndexPath. En fait, j’ai personnellement voté récemment 2 réponses parce que je trouve que cela fonctionne, mais dans mon cas, c’est la façon la plus propre de le faire.

[self.collectionView performBatchUpdates:^{
     // perform indexpaths insertion
} completion:^(BOOL finished) {
     [self.collectionView reloadData];
}];
3
John Paul Manoza

Swift 3/4 version:

collectionView.collectionViewLayout.invalidateLayout()

Mise en garde!

Si vous modifiez le nombre d'éléments collectionView en même temps (par exemple, vous affichez le pied de page uniquement si toutes les cellules ont été chargées), il plantera . Vous devez d'abord recharger les données pour vous assurer que le nombre d'éléments est le même avant et après invalidateLayout():

collectionView.reloadData()
collectionView.collectionViewLayout.invalidateLayout()
2
KlimczakM

Voici deux façons de le faire.

1 . Créez un modèle modifiable pour sauvegarder les données qui seront éventuellement disponibles. Utilisez KVO dans la classe héritée de UICollectionReusableView pour observer les modifications et mettre à jour la vue d'en-tête avec les nouvelles données à mesure qu'elles sont disponibles.

[model addObserver:headerView
        forKeyPath:@"path_To_Header_Data_I_care_about"
           options:(NSKeyValueObservingOptionNew |
                    NSKeyValueObservingOptionOld)
           context:NULL];

puis implémenter une méthode d'écoute dans la vue d'en-tête

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context

2 . Ajoutez un écouteur de notification à la vue et publiez une notification lorsque les données sont disponibles. L'inconvénient est qu'il s'agit d'une application large et non d'un design épuré.

// place in shared header file
#define HEADER_DATA_AVAILABLE @"Header Data Available Notification Name"

// object can contain userData property which could hole data needed. 
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(headerDataAvailable:) name:HEADER_DATA_AVAILABLE object:nil];

[[NSNotificationCenter defaultCenter] postNotificationName:HEADER_DATA_AVAILABLE object:nil];
2
Samuel

Cette question est très ancienne, mais un moyen simple de le faire est de définir un délai qui couvre le temps d'animation de votre vue et de désactiver l'animation pendant la mise à jour de la vue. En général, une suppression ou une insertion prend environ 0,35 faire: 

 delay(0.35){
            UIView.performWithoutAnimation{
            self.collectionView.reloadSections(NSIndexSet(index: 1))  
            }
1
Victor --------
[UIView performWithoutAnimation:^{
    [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:4]];
}];

[UIView performWithoutAnimation:^{
    [self.collectionView reloadData];
}];
1
ylgwhyh

Voici ce que j'ai fait pour mettre à jour uniquement les en-têtes de section actuellement chargés en mémoire:

  • Ajoutez un faibleToStrong NSMapTable. Lorsque vous créez un en-tête, ajoutez-le en tant que clé faiblement tenue avec l'objet indexPath. Si nous réutilisons l'en-tête, nous mettrons à jour indexPath.
  • Lorsque vous devez mettre à jour les en-têtes, vous pouvez maintenant énumérer les objets/clés de NSMapTable selon vos besoins.

    @interface YourCVController ()
        @property (nonatomic, strong) NSMapTable *sectionHeaders;
    @end

    @implementation YourCVContoller

    - (void)viewDidLoad {
        [super viewDidLoad];
        // This will weakly hold on to the KEYS and strongly hold on to the OBJECTS
        // keys == HeaderView, object == indexPath
        self.sectionHeaders = [NSMapTable weakToStrongObjectsMapTable];
    }

    // Creating a Header. Shove it into our map so we can update on the fly
    - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
    {
        PresentationSectionHeader *header = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"presentationHeader" forIndexPath:indexPath];
        // Shove data into header here
        ...
        // Use header as our weak key. If it goes away we don't care about it
        // Set indexPath as object so we can easily find our indexPath if we need it
        [self.sectionHeaders setObject:indexPath forKey:header];
        return header;
    }

    // Update Received, need to update our headers
    - (void) updateHeaders {
        NSEnumerator *enumerator = self.sectionHeaders.keyEnumerator;
        PresentationSectionHeader *header = nil;
        while ((header = enumerator.nextObject)) {
            // Update the header as needed here
            NSIndexPath *indexPath = [self.sectionHeaders objectForKey:header];
        }
    }

    @end
1
Michael Kernahan