web-dev-qa-db-fra.com

UICollectionView: Comment obtenir la taille d'un élément spécifique après l'avoir définie dans la méthode déléguée

Je bidouille les nouvelles classes UICollectionView et UICollectionViewLayout. J'ai créé une présentation personnalisée, en sous-classant UICollectionViewFlowLayout. 

La taille de mes cellules change de façon dynamique et je règle la taille des éléments à l'aide de la méthode de délégation ci-dessous

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout*)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath {

      NSLog(@"SETTING SIZE FOR ITEM AT INDEX %d", indexPath.row);
      return CGSizeMake(80, 80);
}

Maintenant, sous la méthode prepareLayout de ma classe UICollectionViewFlowLayout personnalisée, je dois accéder à ces variables de taille afin de pouvoir calculer comment les placer et les mettre en cache pour layoutAttributesForItemAtIndexPath.

Cependant, il semble impossible de trouver une propriété sous UICollectionView ou UICollectionViewFlowLayout pour atteindre les tailles d'élément personnalisé que j'ai définies dans la méthode delegate. 

11
SarpErdag

Je l'ai trouvé moi-même. 

Implémenter la classe personnalisée comme sans omettre UICollectionViewDelegateFlowLayout 

@interface SECollectionViewCustomLayout : UICollectionViewFlowLayout 
                                          <UICollectionViewDelegateFlowLayout>

et alors vous pouvez appeler 

CGSize size = [self collectionView:self.collectionView 
                            layout:self 
            sizeForItemAtIndexPath:indexPath];
13
SarpErdag

En regardant les différents fichiers d'en-tête UICollectionView... et en regardant la vidéo WWDC 2012 Session 219 - Vues de collection avancées et création de présentations personnalisées (à partir de 6h50 environ), il semble que le modèle de délégué extensible tire parti de la frappe dynamique pour assurer la mise en page peut accéder correctement à ses méthodes de délégué étendu.


En bref...

  1. Si vous définissez une présentation personnalisée avec son propre délégué, définissez ce protocole de délégation dans le fichier d'en-tête de la présentation.
  2. Votre objet délégué (généralement la UI(Collection)ViewController qui gère la vue de collection) doit se déclarer lui-même pour la prise en charge de ce protocole personnalisé.
    • Dans le cas où votre mise en page est simplement une UICollectionViewFlowLayout ou une sous-classe de celle-ci, cela signifie simplement que vous déclarez la conformité à UICollectionViewDelegateFlowLayout.
    • N'hésitez pas à faire cela dans votre extension de classe dans le fichier .m si vous préférez ne pas #import l'en-tête de présentation dans l'interface du délégué.
  3. Pour accéder aux méthodes de délégation à partir de la présentation, appelez le délégué de la vue de collection.
    • Utilisez la propriété collectionView de la présentation et convertissez le délégué en un objet conforme au protocole requis pour convaincre le compilateur.
    • N'oubliez pas de vérifier que le délégué respondsToSelector: est comme d'habitude avant d'appeler des méthodes de délégué facultatives. En fait, si vous le souhaitez, il n'y a pas de mal à le faire pour toutes les méthodes, car le transtypage signifie qu'il n'y a pas de garantie d'exécution au délégué implémentera même les méthodes requises.


Dans du code...

Donc, si vous implémentez une mise en page personnalisée nécessitant un délégué pour certaines de ses informations, votre en-tête pourrait ressembler à ceci:

@protocol CollectionViewDelegateCustomLayout <UICollectionViewDelegate>
- (BOOL)collectionView:(UICollectionView *)collectionView
                layout:(UICollectionViewLayout *)layout
shouldDoSomethingMindblowingAtIndexPath:(NSIndexPath *)indexPath;
@end

@interface CustomLayout : UICollectionViewLayout
// ...
@end


Votre délégué déclare la conformité (je l’ai déjà fait dans le fichier d’application):

#import "CustomLayout.h"

@interface MyCollectionViewController () <CollectionViewDelegateCustomLayout>
@end

@implementation
// ...
- (BOOL)collectionView:(UICollectionView *)collectionView
                layout:(UICollectionViewLayout *)layout
shouldDoSomethingMindblowingAtIndexPath:(NSIndexPath *)indexPath
{
    return [self canDoSomethingMindblowing];
}
// ...
@end


Et dans l'implémentation de votre mise en page, vous accédez à la méthode comme ceci:

BOOL blowMind;
if ([self.collectionView.delegate respondsToSelector:@selecor(collectionView:layout:shouldDoSomethingMindblowingAtIndexPath:)]) {
    blowMind = [(id<CollectionViewDelegateCustomLayout>)self.collectionView.delegate collectionView:self.collectionView
                                                                                             layout:self
                                                            shouldDoSomethingMindblowingAtIndexPath:indexPath];
} else {
    // Perhaps the layout also has a property for this, if the delegate
    // doesn't support dynamic layout properties...?
    // blowMind = self.blowMind;
}

Notez qu'il est sans danger de transtyper ici, car nous vérifions de toute façon que le délégué répond à cette méthode au préalable.


La preuve...

Ce n'est que spéculation, mais je suppose que c'est la façon dont Apple gère le protocole UICollectionViewDelegateFlowLayout.

  • Il n'y a pas de propriété delegate dans la présentation du flux, les appels doivent donc passer par le délégué de la vue de collection.
  • UICollectionViewController ne se conforme pas publiquement au délégué de disposition de flux étendu (et je doute que ce soit le cas dans un autre en-tête privé).
  • UICollectionView 's delegate La propriété déclare uniquement la conformité au protocole' base 'UICollectionViewDelegate. Encore une fois, je doute qu’une sous-classe/catégorie privée de UICollectionView soit utilisée par la structure de flux pour éviter le recours à la conversion de type. Pour ajouter du poids à ce point, Apple déconseille de sous-classer UICollectionView dans la documentation (Guide de programmation Collection View pour iOS: Création de présentations personnalisées ):

Évitez de sous-classer UICollectionView. La vue Collection a peu ou pas d’apparence. Au lieu de cela, il extrait toutes ses vues de votre objet de source de données et toutes les informations relatives à la présentation de l'objet de présentation.

Alors on y va. Pas compliqué, mais vaut la peine de savoir comment le faire de manière à respecter les paradigmes.

8
Stuart

Il existe une version Swift:

self.collectionView(self.collectionView, layout: self.collectionView.collectionViewLayout, sizeForItemAtIndexPath: indexPath)
6
tounaobun

Consultez UICollectionView-FlowLayout sur GitHub. Même idée, cela facilite l'accès aux méthodes de délégation étendues de flowLayout.

4
Charlie Elliott

Dans mon cas, tout ce qui concerne la disposition, la disposition des cellules, etc. est défini dans nib pour UIViewController et nib distinct pour UICollectionViewCell. MyCollectionViewCell contient UIImageView avec autolayout à la cellule avec remplissage/marges mais de forme carrée. 

J'ai besoin d'icônes rondes à la place mais je ne veux pas prendre en compte la pointe que j'utilise pour iPhone ou pour iPad (j'ai des plumes séparées pour les appareils et pour l'orientation). 

Je ne souhaite pas implémenter @selector(collectionView:layout:sizeForItemAtIndexPath:) dans mon contrôleur de vue.

Donc, dans collectionView:cellForItemAtIndexPath: , Je peux simplement utiliser 

CGSize size = cell.imageView.bounds.size;
cell.imageView.layer.masksToBounds = YES;
cell.imageView.layer.cornerRadius = size.height/2.0;

Parce que collectionView:layout:sizeForItemAtIndexPath: appelle avant collectionView:cellForItemAtIndexPath: et que la mise en page soit terminée.

Vous pouvez vérifier les avatars ronds en bas

 enter image description here

0
WINSergey

Pour les lecteurs ultérieurs, IOS 7 a été défini par UICollectionViewFlowLayout.

0
tomjpsun