web-dev-qa-db-fra.com

Avertissement concernant la taille de UICollectionViewFlowLayout lors de la rotation d'un périphérique en mode paysage

Nous utilisons UICollectionView pour afficher les cellules qui couvrent le plein écran (moins l'état et la barre de navigation). La taille de la cellule est définie à partir de self.collectionView.bounds.size:

- (void)viewWillAppear:(BOOL)animated
{
    //
    // value isn't correct with the top bars until here
    //
    CGSize tmpSize = self.collectionView.bounds.size;
    _currentCellSize = CGSizeMake( (tmpSize.width), (tmpSize.height));
}

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

    return _currentCellSize;
}

Ceci définit le dimensionnement correct pour chaque périphérique. Chaque cellule est définie pour ne pas avoir d'encart et la mise en page n'a pas d'en-tête ni de pied de page. Cependant, lorsque nous passons de portrait en paysage, nous obtenons la "plainte" suivante:

the behavior of the UICollectionViewFlowLayout is not defined because:
the item height must be less that the height of the UICollectionView minus the section insets top and bottom values.

Maintenant, je comprends cette erreur. Cependant, nous réinitialisons la taille de la cellule et utilisons les schémas de flux intégrés dans la transition de rotation:

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    //self.collectionView.bounds are still the last size...not the new size here
}
- (void)didRotateFromInterfaceOrientation: UIInterfaceOrientation)fromInterfaceOrientation
{
    CGSize tmpSize = self.collectionView.bounds.size;
    _currentCellSize = CGSizeMake( (tmpSize.width), (tmpSize.height));
    [self.collectionView performBatchUpdates:nil completion:nil];//this will force the redraw/size of the cells.
}

Les cellules sont correctement rendues dans le paysage.

Il semble que la disposition de flux considère l'ancienne taille de cellule (ce qui provoque le problème, car elle sera trop grande), mais lit/rend la nouvelle taille de cellule définie dans didRotateFromInterfaceOrientation.

Y a-t-il un moyen de se débarrasser de la plainte?

Nous avons essayé de trouver un autre crochet lors d'une transition de rotation de périphérique qui a accès à la taille d'écran cible correcte (par rapport à la taille d'écran actuelle) sans aucune chance. La sortie de débogage montre que la plainte se produit après willRotateToInterfaceOrientation mais avant didRotateFromInterfaceOrientation.

Nous avons également vérifié l'évidence; si nous définissons la hauteur de la cellule comme une taille fixe inférieure à la hauteur de l'écran en paysage, le problème ne se pose pas. En outre, la plainte ne se produit pas lors de la rotation de paysage en portrait.

Tout fonctionne bien et rend correctement. Cependant cette plainte nous inquiète. Quelqu'un d'autre a des idées ou des solutions?

36
Mikel Nelson

Je recevais le même avertissement. Insatisfait de l'approche "reloadData", j'ai découvert qu'appeler [self.collectionView.collectionViewFlowLayout invalidateLayout] avant définir le cadre de la vue de collection a fait taire l'avertissement et généré les résultats attendus. 

36
Chris Hinkle

Ne pas jeter une autre crevette sur ce barbie chargé, mais non accepté.

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

    [collectionView setContentInset:UIEdgeInsetsMake(0, 0, 0, 0)];
    return CGSizeMake(100, collectionView.frame.size.height);
}

Définir les encarts de contenu juste avant de renvoyer la taille de la cellule a été très utile.

Remarque:

J'utilise une vue conteneur dans un storyboard pour charger la vue collection dans un UIViewController. J'ai essayé de définir ceci sur l'objet flowLayout dans le storyboard. La vue de la collection dans le storyboard. Et substituer l'un des UICollectionViewDelegateFlowLayout; bien que je ne me souvienne plus lequel. Je ne sais pas non plus si cela fonctionnera pour une mise en page verticale.

9
Josh Bruce

Dans 

[UIViewController willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration]

J'ai appelé le, [UICollectionViewLayout invalidateLayout] et semble bien fonctionner.

6
somebody

Je l'ai résolu.

Vous devriez simplement laisser la hauteur de flowLayout inférieure à collcetionView.frame.size.height.

 [flowLayout setItemSize:CGSizeMake(width, height)];


 collectionView.frame = CGRectMake(12, 380, 290, 80);
4
Donny

Beaucoup de solutions suggèrent d'ajouter invalidateLayout à willAnimateRotationToInterfaceOrientation - mais ceci est obsolète depuis iOS 8.

Pour iOS 8 et versions ultérieures, utilisez:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    [self.collectionView.collectionViewLayout invalidateLayout];
}

Grâce au commentaire de @ user7097242, voici une version de Swift4:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    self.collectionView.collectionViewLayout.invalidateLayout()
}
4
JosephH

Cela fonctionne pour moi: (et espère que cela fonctionne également pour vous!)

- (void)viewWillLayoutSubviews 
{     
     self.flowLayout.itemSize = CGSizeMake(self.collectionView.bounds.size.width/4,self.collectionView.bounds.size.height);    
     [self.flowLayout invalidateLayout];
}

- (void)viewDidLayoutSubviews     
{
    self.flowLayout.itemSize = CGSizeMake(self.collectionView.bounds.size.width/4,self.collectionView.bounds.size.height); 
}
2
mayqiyue

Je viens de rencontrer et résoudre le même problème. Étant donné que ma solution va plus dans le sens de ce que vous demandiez et ne correspond à aucune réponse existante, je la poste ici.

#define NUMBER_OF_CELLS_PER_ROW 1

- (UICollectionViewFlowLayout *)flowLayout {
    return (UICollectionViewFlowLayout *)self.collectionViewLayout;
}

- (CGSize)itemSizeInCurrentOrientation {
    CGFloat windowWidth = self.collectionView.window.bounds.size.width;

    CGFloat width = (windowWidth - (self.flowLayout.minimumInteritemSpacing * (NUMBER_OF_CELLS_PER_ROW - 1)) - self.flowLayout.sectionInset.left - self.flowLayout.sectionInset.right)/NUMBER_OF_CELLS_PER_ROW;

    CGFloat height = 80.0f;

   return CGSizeMake(width, height);
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    [self.flowLayout invalidateLayout];
    self.flowLayout.itemSize = [self itemSizeInCurrentOrientation];
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
    // Now that the rotation is complete, load the cells.
    [self.collectionView reloadData];
}
2
lambmj

J'ai rencontré le même problème. Si la vue Collection était affichée en orientation portrait, les cellules disparaissaient lors de la rotation en paysage. J'ai fait une combinaison des autres réponses ici pour résoudre ce problème. 

J'ai configuré mon affichage (celui qui contient UICollectionView) pour recevoir la notification UIDeviceOrientationDidChangeNotification. Dans la méthode qui répond à cette notification, une fois le cadre UICollectionView ajusté, j'ai procédé comme suit:

- (void)orientationChanged:(NSNotification *)notification
{
    CGSize size = (CGSize){self.frame.size.width - 2*kContentMargin, self.frame.size.height - 2*kContentMargin};
    [self.collectionView.collectionViewLayout setItemSize:size];

    [self.collectionView.collectionViewLayout invalidateLayout];
    [self.collectionView reloadData];
}

Notez que le cadre de UICollectionView est défini automatiquement lors de la rotation car son masque de redimensionnement est défini sur lors de l'initialisation:

self.collectionView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
2
Alfie Hanssen

Cela a résolu le problème pour moi:

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    [self.collectionView.collectionViewLayout invalidateLayout];
}

Et j'utilise cette méthode de délégué pour définir la taille:

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

Et je peux à partir de là utiliser ceci avec le bon cadre après rotation:

self.collectionView.frame.size
2
ullstrm

Ma solution était aussi simple que de décocher la case "Ajuster les incrustations de la vue de défilement" du contrôleur de vue dans IB, car j'avais besoin que ma barre de navigation soit translucide.

2

J'ai fait face au même problème ... Voici comment je l'ai résolu. J'espère que ça aide

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(    NSTimeInterval)duration
{
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];

    [self.collectionView reloadData];
}
1
xun

Essaye ça...

UICollectionViewFlowLayout *layout = (id) self.collectionView.collectionViewLayout;

layout.itemSize = CGSizeMake(0.1, 0.1);

Cela fonctionne pour moi.

1
Masataga.Takashi

Essayer de trouver une solution pour faire taire ces avertissements sur iOS 7 se révélait difficile pour moi. J'ai fini par recourir au sous-classement de ma UICollectionView et ajouté le code suivant.

- (void)setFrame:(CGRect)frame
{
    if (!iOS8 && (frame.size.width != self.frame.size.width))
    {
        [self.collectionViewLayout invalidateLayout];
    }

    [super setFrame:frame];
}

Certains voudront peut-être faire une vérification de taille entière avec CGSizeEqualToSize().

0
Ricky

essayez à la fin de didRotateFromInterfaceOrientation:

[self.collectionView setNeedsLayout]

si cela ne fonctionne pas, essayez de déplacer tous ces éléments de didRotateFromInterfaceOrientation vers willRotateToInterfaceOrientation

0
J.Williams

J'ai utilisé une variable UICollectionViewFlowLayoutInvalidationContext dans laquelle je calcule le nouveau décalage de sorte qu'il conserve le même décalage de contenu. Ma propre fonction collectionViewSizeForOrientation: renvoie la taille appropriée. Ce n'est pas parfait, mais au moins ce n'est pas sommaire:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    CGSize fromCollectionViewSize = [self collectionViewSizeForOrientation:[self interfaceOrientation]];
    CGSize toCollectionViewSize = [self collectionViewSizeForOrientation:toInterfaceOrientation];

    CGFloat currentPage = [_collectionView contentOffset].x / [_collectionView bounds].size.width;
    NSInteger itemCount = [_collectionView numberOfItemsInSection:0];

    UICollectionViewFlowLayoutInvalidationContext *invalidationContext = [[UICollectionViewFlowLayoutInvalidationContext alloc] init];

    [invalidationContext setContentSizeAdjustment:CGSizeMake(toCollectionViewSize.width * itemCount - fromCollectionViewSize.width * itemCount, toCollectionViewSize.height - fromCollectionViewSize.height)];
    [invalidationContext setContentOffsetAdjustment:CGPointMake(currentPage * toCollectionViewSize.width - [_collectionView contentOffset].x, 0)];

    [[_collectionView collectionViewLayout] invalidateLayoutWithContext:invalidationContext];

    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}

collectionViewSizeForOrientation: dans mon cas est le suivant, en supposant que les incrustations et l'espacement des éléments soient égaux à 0:

- (CGSize)collectionViewSizeForOrientation:(UIInterfaceOrientation)orientation
{
    CGSize screenSize = [[UIScreen mainScreen] bounds].size;

    CGFloat width = UIInterfaceOrientationIsLandscape(orientation) ? MAX(screenSize.width, screenSize.height) : MIN(screenSize.width, screenSize.height);
    CGFloat height = UIInterfaceOrientationIsLandscape(orientation) ? MIN(screenSize.width, screenSize.height) : MAX(screenSize.width, screenSize.height);

    return CGSizeMake(width, height);

}

0
MrJre

Vous pouvez sous-classer UICollectionView et remplacer setBounds:. Avant d'appeler [super setBounds:], la taille de l'élément peut être ajustée aux nouvelles limites.
Vous devriez vérifier si la taille des limites a changé, car setBounds: est également appelé lors du défilement.

0
Eddie Gasparian

Remarque: dans mon cas, j'ai découvert que nous définissions la propriétépreferredContentSizede la propriété UIViewController en question. Si tel est votre cas, vous devrez peut-être utiliser la méthode suivante du protocole UICollectionViewDelegateFlowLayout:

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    CGSize size = collectionViewLayout.collectionView.bounds.size;
    ...
    return size;
}
0
Coach Roebuck

Le code suivant l'a corrigé pour moi:

-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
return self.collectionView.frame.size;
}
0
Obj-Swift

Je sais que c'est une vieille question, mais je viens d'avoir le même problème et j'ai passé une heure à le résoudre. Mon problème était que, il semblerait que la taille de trame d'UICollectionView soit toujours incorrecte (la hauteur ne correspond pas au conteneur) alors que je définissais la taille de la trame juste avant l'appel du délégué de modèle UICollectionView Flow. Donc, je redéfinis la taille de trame UICollectionView sur la méthode:

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

Et ça a fait l'affaire. La hauteur est maintenant affichée correctement et l'avertissement est parti.

0
tyegah123