web-dev-qa-db-fra.com

UICollectionViewFlowLayout n'invalide pas le droit lors du changement d'orientation

j'ai un UICollectionView avec un UICollectionViewFlowLayout. J'implémente également le protocole UICollectionViewDelegateFlowLayout.

Dans ma source de données, j'ai un tas d'UIViewControllers qui répondent à un protocole personnalisé afin que je puisse demander leur taille et d'autres choses.

Dans le délégué FlowLayout quand il demande le sizeForItemAtIndexPath :, je retourne la taille de l'élément que j'obtiens de mon protocole. Les ViewControllers qui implémentent mon protocole renvoient une taille d'élément différente en fonction de l'orientation.

Maintenant, si je change l'orientation de l'appareil de portrait en paysage, il n'y a pas de problème (les éléments sont plus grands en paysage) mais si je la change en arrière, j'obtiens cet avertissement:

    the item width must be less that the width of the UICollectionView minus the section insets left and right values.
    Please check the values return by the delegate.

Cela fonctionne toujours, mais je n'aime pas qu'il reçoive des avertissements, alors vous pouvez peut-être me dire ce que je fais mal. Il y a aussi un autre problème. Si je ne dis pas à ma collectionViews collectionViewLayout d'invalider dans willAnimateRotationToInterfaceOrientation: le sizeForItemAtIndexPath: n'est jamais appelé.

J'espère que vous comprenez ce que je veux dire. Si vous avez besoin d'informations supplémentaires, faites le moi savoir :)

22
xxtesaxx

Une solution consiste à utiliser KVO et à écouter une modification du cadre de la vue d'ensemble pour votre vue de collection. L'appel de -reloadData fonctionnera et supprimera les avertissements. Il y a un problème de timing sous-jacent ici ...

// -loadView
[self.view addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];

// -dealloc
[self.view removeObserver:self forKeyPath:@"frame"];

// KVO Method
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    [collectionView_ reloadData];
}
1
kwigbo

Cette réponse est en retard, mais la réponse acceptée n'a pas fonctionné pour moi. Je sympathise avec l'OP en souhaitant un UICollectionViewFlowLayout à tirer et oublier. Je suggère que l'invalidation de la disposition dans le contrôleur de vue est en fait la meilleure solution.

Je voulais une seule ligne de cellules à défilement horizontal, centrée dans la vue, à la fois en portrait et en paysage.

J'ai sous-classé UICollectionViewFlowLayout.

J'ai outrepassé prepareLayout pour recalculer les encarts, puis j'appelle [super prepareLayout].

J'ai outrepassé le getter pour collectionViewContentSize pour faire certain la taille du contenu était correcte.

La mise en page n'a pas été invalidée par elle-même, même si les limites changeaient avec la réorientation.

- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    // does the superclass do anything at this point?
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];

    // do whatever else you need before rotating toInterfaceOrientation

    // tell the layout to recalculate
    [self.collectionViewLayout invalidateLayout];
}

UICollectionViewFlowLayout maintient la position de défilement entre les orientations. La même cellule sera centrée niquement si elle est carrée.

36
Bob Wakefield

J'ai utilisé une combinaison des deux réponses ici pour faire ce que nous essayons tous de faire, c'est vraiment faire en sorte qu'une vue de collection ressemble à une vue de table mais pas utiliser UITableView et obtenir des éléments dans une seule colonne (le plus probable)

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

    [self.collectionView.collectionViewLayout invalidateLayout];
}
8
cynistersix

Une variation sur la réponse de @ meelawsh, mais dans Swift, et sans self.sizeForCollectionView propriété:

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {

    super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)

    guard let collectionView = self.collectionView else {
        return
    }

    if size.width > collectionView.frame.width {
        // bigger

        let animation: (UIViewControllerTransitionCoordinatorContext)->(Void) = {
            context in
            collectionView.collectionViewLayout.invalidateLayout()
        }

        coordinator.animateAlongsideTransition(animation, completion: nil)
    } else {
        // smaller
        collectionView.collectionViewLayout.invalidateLayout()
    }
}
3
Kenny Winker

iOS8: la seule façon de faire taire l'avertissement est d'appeler invalidate pendant la transition lors de la rotation vers une taille plus grande et avant la transition lors de la rotation vers une taille plus petite.

    self.sizeForCollectionView = size; //used to determine collectionview size in sizeForItem
    if (size.width <= size.height) {
        [self.collectionView.collectionViewLayout invalidateLayout];
    }

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        if (size.width > size.height) {
            [self.collectionView.collectionViewLayout invalidateLayout];
        }
    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
    }];
2
meelawsh

Je ne sais pas si cela peut aider quelqu'un, mais aucune des dernières réponses ne m'aide car j'utilise un UIViews dans un UIScrollView.

J'ouvre les UIViews par programme en fonction de certains événements. Je ne peux donc pas utiliser - (void) willRotateToInterfaceOrientation: et invalidateLayout ou reloadData car:
1. invalidateLayout "se produit lors du prochain cycle de mise à jour de la mise en page."
2. reloadData "Pour plus d'efficacité, la vue de collection n'affiche que les cellules et les vues supplémentaires qui sont visibles."

Ce qui a fonctionné pour moi a été d'utiliser la largeur du parent de la collectionView (une UIView) dans:

-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
int currentWidth = self.frame.size.width;

UIEdgeInsets sectionInset = [(UICollectionViewFlowLayout *)collectionView.collectionViewLayout sectionInset];
int fixedWidth = currentWidth - (sectionInset.left + sectionInset.right);


Pour une raison quelconque, le cadre collectionView avait la largeur du portrait au moment d'appeler cette fonction ... en utilisant int currentWidth = self.frame.width aidez-moi à le réparer.

2
MiguelHincapieC

La réponse de @ 0mahc0 a fonctionné pour moi, mais seulement partiellement.

Je n'ai pas le problème de rotation, car je force mon application à être uniquement en mode Portrait, mais ma solution peut aider d'autres personnes ayant le même problème.

L'avertissement s'affiche chaque fois que la vue apparaît. La raison de l'avertissement est très claire, nous avons défini des encarts de section à gauche et à droite de UICollectionView. Vérifiez le constructeur d'interface et vous verrez dans les mesures les valeurs définies.

La solution de @ 0mahc0 fonctionne, mais je veux étirer la cellule à la largeur maximale, j'ai donc supprimé les encarts de section, sinon j'aurai une "marge" à gauche et à droite.

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
    return UIEdgeInsetsZero;
}

Au lieu d'implémenter la méthode déléguée, vous pouvez accéder au générateur d'interface et modifier la valeur en zéro des encarts droit et gauche.

1
Pedro Romão