74 votes

En gardant le contentOffset dans un UICollectionView lors de la rotation de l'Interface de l'Orientation

Je suis en train de gérer l'interface de changements d'orientation dans un UICollectionViewController. Ce que je suis en train de réaliser est que je veux avoir le même contentOffset après une interface de rotation. Sens, qu'il devrait être changé correspondant au ratio des limites de changement.

Le démarrage en mode portrait avec le décalage du contenu de {limites.taille.largeur * 2, 0} ...

UICollectionView in portait

... devrait se traduire pour le contenu du décalage dans le paysage aussi avec {limites.taille.largeur * 2, 0} (et vice versa).

UICollectionView in landscape

Le calcul de la nouvelle décalage n'est pas le problème, mais ne sais pas où (ou quand) définir, pour obtenir une animation fluide. Ce que je fais donc le tarif est d'invalider la mise en page en willRotateToInterfaceOrientation:duration: et de réinitialiser le contenu de décalage en didRotateFromInterfaceOrientation::

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                                duration:(NSTimeInterval)duration;
{
    self.scrollPositionBeforeRotation = CGPointMake(self.collectionView.contentOffset.x / self.collectionView.contentSize.width,
                                                    self.collectionView.contentOffset.y / self.collectionView.contentSize.height);
    [self.collectionView.collectionViewLayout invalidateLayout];
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
{
    CGPoint newContentOffset = CGPointMake(self.scrollPositionBeforeRotation.x * self.collectionView.contentSize.width,
                                           self.scrollPositionBeforeRotation.y * self.collectionView.contentSize.height);
    [self.collectionView newContentOffset animated:YES];
}

Cela modifie le contenu de décalage après la rotation.

Comment puis-je le mettre lors de la rotation? J'ai essayé de définir le nouveau contenu de décalage en willAnimateRotationToInterfaceOrientation:duration: , mais cela donne un très étrange comportement.

Un exemple peut être trouvé dans mon Projet sur GitHub.

18voto

Reflejo Points 391

Solution 1, "cassez"

Si ce que vous avez besoin est seulement pour s'assurer que contentOffset se termine dans une bonne position, vous pouvez créer une sous-classe de UICollectionViewLayout et de mettre en œuvre targetContentOffsetForProposedContentoffset: méthode. Par exemple, vous pourriez faire quelque chose comme cela pour calculer la page:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
{
    NSInteger page = ceil(proposedContentOffset.x / [self.collectionView frame].size.width);
    return CGPointMake(page * [self.collectionView frame].size.width, 0);
}

Mais le problème que vous devrez faire face est que l'animation pour que la transition est extrêmement bizarre. Ce que je fais sur mon cas (ce qui est presque la même que la vôtre) est:

La Solution 2, "l'animation en douceur"

1) j'ai d'Abord définir la taille de la cellule, qui peut être gérée par collectionView:mise en page:sizeForItemAtIndexPath: délégué méthode comme suit:

- (CGSize)collectionView:(UICollectionView *)collectionView
                  layout:(UICollectionViewLayout  *)collectionViewLayout
  sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return [self.view bounds].size;
}

Notez que [auto.afficher les limites] va changer en fonction de l'appareil de rotation.

2) Lorsque l'appareil est sur le point de tourner, je suis en ajoutant une imageView sur le dessus de l'affichage de la collection avec tous le redimensionnement des masques. Il s'agit en fait de masquer le collectionView étrangeté (parce qu'il est sur le dessus de celui-ci) et, depuis le willRotatoToInterfaceOrientation: méthode est appelée à l'intérieur d'un bloc d'animation, il tournera en conséquence. Je suis aussi en gardant la prochaine contentOffset selon l'montré indexPath afin que je puisse corriger le contentOffset une fois que la rotation se fait:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                                duration:(NSTimeInterval)duration
{
    // Gets the first (and only) visible cell.
    NSIndexPath *indexPath = [[self.collectionView indexPathsForVisibleItems] firstObject];
    KSPhotoViewCell *cell = (id)[self.collectionView cellForItemAtIndexPath:indexPath];

    // Creates a temporary imageView that will occupy the full screen and rotate.
    UIImageView *imageView = [[UIImageView alloc] initWithImage:[[cell imageView] image]];
    [imageView setFrame:[self.view bounds]];
    [imageView setTag:kTemporaryImageTag];
    [imageView setBackgroundColor:[UIColor blackColor]];
    [imageView setContentMode:[[cell imageView] contentMode]];
    [imageView setAutoresizingMask:0xff];
    [self.view insertSubview:imageView aboveSubview:self.collectionView];

    // Invalidate layout and calculate (next) contentOffset.
    contentOffsetAfterRotation = CGPointMake(indexPath.item * [self.view bounds].size.height, 0);
    [[self.collectionView collectionViewLayout] invalidateLayout];
}

Notez que mon sous-classe de UICollectionViewCell a un public imageView de la propriété.

3) Enfin, la dernière étape consiste à "snap" la le décalage du contenu à une page valide et la suppression de la temporaire de l'imageview.

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [self.collectionView setContentOffset:contentOffsetAfterRotation];
    [[self.view viewWithTag:kTemporaryImageTag] removeFromSuperview];
}

15voto

troppoli Points 50

Le "snap" la réponse ci-dessus n'a pas fonctionné pour moi, comme souvent, il n'est pas sur le point qui était à l'écran avant de le faire pivoter. J'ai donc tiré un flux de mise en page qui utilise un élément activé (si défini) pour le calcul de la teneur de l'offset. J'ai mis l'article dans willAnimateRotationToInterfaceOrientation et clair dans didRotateFromInterfaceOrientation. L'encart de l'ajustement semble être besoin sur IOS7 parce que la Collecte de vue mise en page, sous la barre du haut.

@interface HintedFlowLayout : UICollectionViewFlowLayout
@property (strong)NSIndexPath* pathForFocusItem;
@end

@implementation HintedFlowLayout

-(CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
{
    if (self.pathForFocusItem) {
        UICollectionViewLayoutAttributes* layoutAttrs = [self layoutAttributesForItemAtIndexPath:self.pathForFocusItem];
        return CGPointMake(layoutAttrs.frame.origin.x - self.collectionView.contentInset.left, layoutAttrs.frame.origin.y-self.collectionView.contentInset.top);
    }else{
        return [super targetContentOffsetForProposedContentOffset:proposedContentOffset];
    }
}
@end

8voto

2cupsOfTech Points 1541

Je pense que la bonne solution est de remplacer - (CGPoint)targetContentOffsetForProposedContentoffset:(CGPoint)proposedContentOffset méthode dans une sous-classé UICollectionViewFlowLayout

À partir de la documentation:

Lors de la présentation des mises à jour, ou lors de la transition entre les mises en page, la vue de collection appelle cette méthode pour vous donner l'occasion de modifier le contenu de décalage à utiliser à la fin de l'animation. Vous pouvez utiliser cette méthode si les animations ou de transition les éléments à être positionnée de façon à ce que n'est pas optimale pour votre de la conception.

0voto

Goodsquirrel Points 193

Vous pouvez masquer la collectionView au cours de sa (mauvaise) de l'animation et de montrer un espace réservé à l'affichage de la cellule qui tourne correctement, plutôt.

Pour une simple galerie de photos, j'ai trouvé une façon de faire qui semble plutôt bonne. Voir ma réponse ici: Comment faire pivoter une UICollectionView similaire à l'app photos et de garder la vue actuelle centrée?

0voto

Itsraininggold Points 33

Ma manière est d'utiliser un UICollectionViewFlowlayout objet.

Définir la ojbect espacement de ligne si il défile horizontalement.

[flowLayout setMinimumLineSpacing:26.0f];

Définir ses interitem espacement si elle défile verticalement.

[flowLayout setMinimumInteritemSpacing:0.0f];

Avis il se comporte de façon différente lorsque vous faites pivoter l'écran. Dans mon cas, je l'ai fait défiler horizontalement pour minimumlinespacing est de 26,0 f. Ensuite, il semble horrible quand il tourne à un paysage de direction. Je dois vérifier la rotation et de l'ensemble minimumlinespacing pour que la direction 0.0 f pour le rendre droit.

Ça y est! Simple.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X