web-dev-qa-db-fra.com

Conserver le contentOffset dans un UICollectionView tout en faisant pivoter Interface Interface

J'essaie de gérer les changements d'orientation d'interface dans un UICollectionViewController. Ce que j'essaie de faire, c'est que je veuille avoir le même contentOffset après une rotation d'interface. Cela signifie qu’il devrait être modifié en fonction du rapport de modification des limites.

Commençant en mode portrait avec un décalage de contenu de {bounds.size.width * 2, 0}…

UICollectionView in portait

… Devrait entraîner un décalage du contenu dans paysage également avec {bounds.size.width * 2, 0} (et vice versa).

UICollectionView in landscape

Le calcul du nouveau décalage n’est pas un problème, mais vous ne savez ni où (ni quand) le définir pour obtenir une animation fluide. Ce que je fais si tarif invalide la mise en page dans willRotateToInterfaceOrientation:duration: et réinitialise le décalage de contenu dans 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 après la rotation.

Comment puis-je le définir pendant la rotation? J'ai essayé de définir le nouveau décalage de contenu dans willAnimateRotationToInterfaceOrientation:duration:, mais cela entraîne un comportement très étrange.

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

66
Tobias Kräntzer

Voici le code dans Swift 3.1 et même fonctionne pour Swift 4.2

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
{
    super.viewWillTransition(to: size, with: coordinator)
    let offset = self.collectionView?.contentOffset;
    let width  = self.collectionView?.bounds.size.width;

    let index     = round(offset!.x / width!);
    let newOffset = CGPoint(x: index * size.width, y: offset!.y)

    self.collectionView?.setContentOffset(newOffset, animated: false)


    coordinator.animate(alongsideTransition: { (context) in
        self.collectionView?.reloadData()
        self.collectionView?.setContentOffset(newOffset, animated: false)
    }, completion: nil)
}
19
GSK

Solution 1, "il suffit de casser"

Si vous souhaitez uniquement que le contentOffset se termine bien, vous pouvez créer une sous-classe de UICollectionViewLayout et implémenter targetContentOffsetForProposedContentOffset: method. Par exemple, vous pouvez faire quelque chose comme ceci 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 allez rencontrer est que l'animation pour cette transition est extrêmement étrange. Ce que je fais sur mon cas (qui est presque le même que le vôtre) est:

Solution 2, "animation fluide"

1) Je définis d’abord la taille de la cellule, qui peut être gérée par collectionView: layout: sizeForItemAtIndexPath: méthode delegate comme suit:

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

Notez que [self.view bounds] changera en fonction de la rotation du périphérique.

2) Lorsque le périphérique est sur le point de pivoter, j'ajoute une imageView au-dessus de la vue Collection avec tous les masques de redimensionnement. En fait, cette vue masquera l'étrangeté collectionView (car elle se trouve au-dessus de celle-ci) et puisque la méthode willRotatoToInterfaceOrientation: est appelée dans un bloc d'animation, elle pivote en conséquence. Je garde également le prochain contentOffset en fonction du chemin indexPath afin que je puisse corriger le contentOffset une fois la rotation effectuée:

- (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 ma sous-classe de UICollectionViewCell a une propriété imageView publique.

3) Enfin, la dernière étape consiste à "aligner" le contenu décalé sur une page valide et à supprimer la vue temporaire.

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

La réponse "juste en bref" ci-dessus n'a pas fonctionné pour moi car elle ne finissait souvent pas avec l'élément qui était en vue avant la rotation. J'ai donc créé une structure de flux qui utilise un élément de focus (s'il est défini) pour calculer le décalage du contenu. Je place l'élément dans willAnimateRotationToInterfaceOrientation et le supprime dans didRotateFromInterfaceOrientation. L'ajustement d'encart semble être nécessaire sur IOS7 car la vue Collection peut être agencée sous la barre supérieure.

@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
13
troppoli

Pour ceux utilisant iOS 8+, willRotateToInterfaceOrientation et didRotateFromInterfaceOrientation sont obsolètes. 

Vous devriez utiliser ce qui suit maintenant:

/* 
This method is called when the view controller's view's size is changed by its parent (i.e. for the root view controller when its window rotates or is resized). 
If you override this method, you should either call super to propagate the change to children or manually forward the change to children.
*/
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator 
{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        // Update scroll position during rotation animation
        self.collectionView.contentOffset = (CGPoint){contentOffsetX, contentOffsetY};
    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        // Whatever you want to do when the rotation animation is done
    }];
}

Swift 3:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)

    coordinator.animate(alongsideTransition: { (context:UIViewControllerTransitionCoordinatorContext) in
        // Update scroll position during rotation animation
    }) { (context:UIViewControllerTransitionCoordinatorContext) in
        // Whatever you want to do when the rotation animation is done
    }
}
11
Neilc

Je pense que la bonne solution est de remplacer - (CGPoint) targetContentOffsetForProposedContentOffset: (CGPoint) proposeContentOffset méthode dans une sous-classe UICollectionViewFlowLayout

De la docs:

Pendant les mises à jour de la disposition ou lors de la transition entre les dispositions, le fichier La vue collection appelle cette méthode pour vous donner l'occasion de changez le décalage de contenu proposé pour l'utiliser à la fin de l'animation . Vous pouvez remplacer cette méthode si les animations ou la transition peuvent faire en sorte que les articles soient positionnés de manière à ne pas être optimale pour votre conception.

7
2cupsOfTech

Pour reprendre la solution troppoli , vous pouvez définir le décalage dans votre classe personnalisée sans avoir à vous soucier de l'implémentation du code dans votre contrôleur de vue. prepareForAnimatedBoundsChange devrait être appelé lorsque vous faites pivoter le périphérique, puis finalizeAnimatedBoundsChange après sa rotation.

@interface OrientationFlowLayout ()

@property (strong)NSIndexPath* pathForFocusItem;

@end

@implementation OrientationFlowLayout

- (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];
    }
}

- (void)prepareForAnimatedBoundsChange:(CGRect)oldBounds {
    [super prepareForAnimatedBoundsChange:oldBounds];
    self.pathForFocusItem = [[self.collectionView indexPathsForVisibleItems] firstObject];
}

- (void)finalizeAnimatedBoundsChange {
    [super finalizeAnimatedBoundsChange];
    self.pathForFocusItem = nil;
}

@end
4
Joe

Ce problème m'a aussi un peu dérangé. Les réponses les plus votées semblaient un peu trop compliquées pour moi, alors je les ai juste insensées et modifié l’alpha de la vue de collection, respectivement, avant et après la rotation. Je n'anime pas non plus la mise à jour du contenu.

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
self.collectionView.alpha = 0;
[self.collectionView.collectionViewLayout invalidateLayout];

self.scrollPositionBeforeRotation = CGPointMake(self.collectionView.contentOffset.x / self.collectionView.contentSize.width,
                                                self.collectionView.contentOffset.y / self.collectionView.contentSize.height);
}

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

[self.collectionView setContentOffset:newContentOffset animated:NO];
self.collectionView.alpha = 1;
}

Assez lisse et moins hacky.

4
mafiOSo

J'utilise une variante de fz. réponse (iOS 7 & 8):

Avant la rotation: 

  1. Stocker le chemin d'index visible actuel
  2. Créer un instantané de la collectionView
  3. Placez un UIImageView avec lui au-dessus de la vue de la collection

Après rotation:

  1. Faites défiler jusqu'à l'index stocké
  2. Supprimer la vue de l'image.

    @property (nonatomic) NSIndexPath *indexPath;
    
    - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                                    duration:(NSTimeInterval)duration {
        self.indexPathAfterRotation = [[self.collectionView indexPathsForVisibleItems] firstObject];
    
        // Creates a temporary imageView that will occupy the full screen and rotate.
        UIGraphicsBeginImageContextWithOptions(self.collectionView.bounds.size, YES, 0);
        [self.collectionView drawViewHierarchyInRect:self.collectionView.bounds afterScreenUpdates:YES];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    
        UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
        [imageView setFrame:[self.collectionView bounds]];
        [imageView setTag:kTemporaryImageTag];
        [imageView setBackgroundColor:[UIColor blackColor]];
        [imageView setContentMode:UIViewContentModeCenter];
        [imageView setAutoresizingMask:0xff];
        [self.view insertSubview:imageView aboveSubview:self.collectionView];
    
        [[self.collectionView collectionViewLayout] invalidateLayout];
    }
    
    - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
        [self.collectionView scrollToItemAtIndexPath:self.indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
    
        [[self.view viewWithTag:kTemporaryImageTag] removeFromSuperview];
    }
    
4
dulgan

Cela fonctionne comme un charme:

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

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {

    int currentPage = collectionMedia.contentOffset.x / collectionMedia.bounds.size.width;
    float width = collectionMedia.bounds.size.height;

    [UIView animateWithDuration:duration animations:^{
        [self.collectionMedia setContentOffset:CGPointMake(width * currentPage, 0.0) animated:NO];
        [[self.collectionMedia collectionViewLayout] invalidateLayout];
}];
}
2
user3676554

Qu'est-ce que le travail pour moi est la suivante:

  1. Définissez la taille de vos cellules my my à partir de votre méthode my UICollectionViewDelegateFlowLayout

    func collectionView(collectionView: UICollectionView!, layout collectionViewLayout: UICollectionViewLayout!, sizeForItemAtIndexPath indexPath: NSIndexPath!) -> CGSize
    {
        return collectionView.bounds.size
    }
    
  2. Après cela, j'implémente willRotateToInterfaceOrientationToInterfaceOrientation:duration: comme ceci

    override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) 
    {
        let currentPage = Int(collectionView.contentOffset.x / collectionView.bounds.size.width)
    
        var width = collectionView.bounds.size.height
        UIView.animateWithDuration(duration) {
            self.collectionView.setContentOffset(CGPointMake(width * CGFloat(currentPage), 0.0), animated: false)
            self.collectionView.collectionViewLayout.invalidateLayout()
        }
    }
    

Le code ci-dessus est dans Swift mais vous obtenez le point et il est facile de "traduire"

1
Mihai Fratu

dans Swift 3.

vous devez suivre quel élément de cellule (page) est présenté avant une rotation par indexPath.item, la coordonnée x ou quelque chose d'autre. Ensuite, dans votre UICollectionView:

override func collectionView(_ collectionView: UICollectionView, targetContentOffsetForProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {

    let page:CGFloat = pageNumber // your tracked page number eg. 1.0
    return CGPoint(x: page * collectionView.frame.size.width, y: -(topInset))
    //the 'y' value would be '0' if you don't have any top EdgeInset
}

Dans mon cas, la structure de viewDidLayoutSubviews () est invalidée de sorte que collectionView.frame.size.width correspond à la largeur de la vue de la collectionVC qui a été pivotée.

1
Kyle KIM

S'il s'avère que l'utilisation de targetContentOffsetForProposedContentOffset ne fonctionne pas dans tous les scénarios, le problème lié à l'utilisation de didRotateFromInterfaceOrientation est le fait qu'il donne des artefacts visuels. Mon code qui fonctionne parfaitement est le suivant:

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
    _indexPathOfFirstCell = [self indexPathsForVisibleItems].firstObject;
}

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
    [super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
    if (_indexPathOfFirstCell) {
        [UIView performWithoutAnimation:^{
            [self scrollToItemAtIndexPath:self->_indexPathOfFirstCell atScrollPosition:UICollectionViewScrollPositionTop animated:NO];
        }];
        _indexPathOfFirstCell = nil;
    }
}

La clé consiste à utiliser la méthode willRotateToInterfaceOrientation pour déterminer la partie de la vue vers laquelle vous souhaitez faire défiler et willAnimationRotationToInterfaceOrientation pour la recalculer lorsque la vue a changé de taille (les limites ont déjà changé lorsque cette méthode est appelée par le framework) effectivement défiler jusqu'à la nouvelle position sans animation. Dans mon code, j'ai utilisé le chemin d'index de la première cellule visuelle pour le faire, mais un pourcentage de contentOffset.y/contentSize.height ferait également le travail de manière légèrement différente.

1
Werner Altewischer

Swift 4.2 sous-classe:

class RotatableCollectionViewFlowLayout: UICollectionViewFlowLayout {

    private var focusedIndexPath: IndexPath?

    override func prepare(forAnimatedBoundsChange oldBounds: CGRect) {
        super.prepare(forAnimatedBoundsChange: oldBounds)
        focusedIndexPath = collectionView?.indexPathsForVisibleItems.first
    }

    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint) -> CGPoint {
        guard let indexPath = focusedIndexPath
            , let attributes = layoutAttributesForItem(at: indexPath)
            , let collectionView = collectionView else {
                return super.targetContentOffset(forProposedContentOffset: proposedContentOffset)
        }
        return CGPoint(x: attributes.frame.Origin.x - collectionView.contentInset.left,
                       y: attributes.frame.Origin.x - collectionView.contentInset.left)
    }

    override func finalizeAnimatedBoundsChange() {
        super.finalizeAnimatedBoundsChange()
        focusedIndexPath = nil
    }
}
0
Bogdan Novikov

Mon moyen est d'utiliser un objet UICollectionViewFlowlayout.

Définissez l’espacement des lignes si vous faites défiler horizontalement. 

[flowLayout setMinimumLineSpacing:26.0f];

Définissez son espacement interitem s’il défile verticalement.

[flowLayout setMinimumInteritemSpacing:0.0f];

Notez qu'il se comporte différemment lorsque vous faites pivoter l'écran. Dans mon cas, je le fais défiler horizontalement donc minimumlinespacing est 26.0f. Ensuite, cela semble horrible quand il tourne dans la direction du paysage. Je dois vérifier la rotation et définir minimum linespacing pour cette direction 0.0f pour le rendre juste. 

C'est tout! Simple.

0
Itsraininggold

J'ai résolu ce problème en suivant les étapes suivantes: 

  1. Calcule le NSIndexPath actuellement affiché  
  2. Désactiver le défilement et la pagination dans UICollectionView  
  3. Appliquer une nouvelle disposition de flux à UICollectionView
  4. Activer le défilement et la pagination dans UICollectionView
  5. Faites défiler UICollectionView vers NSIndexPath actuel

Voici le modèle de code illustrant les étapes ci-dessus: 

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
                            duration:(NSTimeInterval)duration;
{
     //Calculating Current IndexPath
     CGRect visibleRect = (CGRect){.Origin = self.yourCollectionView.contentOffset, .size = self.yourCollectionView.bounds.size};
     CGPoint visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect));
     self.currentIndexPath = [self.yourCollectionView indexPathForItemAtPoint:visiblePoint];

     //Disable Scrolling and Pagination
     [self disableScrolling];

     //Applying New Flow Layout
     [self setupNewFlowLayout];

     //Enable Scrolling and Pagination
     [self enableScrolling];
}

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation;
{
     //You can also call this at the End of `willRotate..` method.
     //Scrolling UICollectionView to current Index Path
     [self.yourCollectionView scrollToItemAtIndexPath:self.currentIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO];
}

- (void) disableScrolling
{
    self.yourCollectionView.scrollEnabled   = false;
    self.yourCollectionView.pagingEnabled   = false;
}

- (void) enableScrolling
{
    self.yourCollectionView.scrollEnabled   = true;
    self.yourCollectionView.pagingEnabled   = true;
}

- (void) setupNewFlowLayout
{
    UICollectionViewFlowLayout* flowLayout = [[UICollectionViewFlowLayout alloc] init];
    flowLayout.sectionInset = UIEdgeInsetsMake(0, 0, 0, 0);
    flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    flowLayout.minimumInteritemSpacing = 0;
    flowLayout.minimumLineSpacing = 0;
    [flowLayout setItemSize:CGSizeMake(EXPECTED_WIDTH, EXPECTED_HEIGHT)];

    [self.yourCollectionView setCollectionViewLayout:flowLayout animated:YES];
    [self.yourCollectionView.collectionViewLayout invalidateLayout];
}

J'espère que ça aide.

0
Salman Khakwani

La réponse "juste en instantané" est la bonne approche et ne nécessite pas de lissage supplémentaire avec les superpositions d'instantanés IMO. Cependant, il existe un problème qui explique pourquoi certaines personnes voient que la bonne page n'est pas sélectionnée dans certains cas. Lors du calcul de la page, vous souhaiterez utiliser la hauteur et non la largeur. Pourquoi? Étant donné que la géométrie de la vue a déjà fait l'objet d'une rotation selon le temps imparti à targetContentOffsetForProposedContentOffset, la largeur correspond à la hauteur. Arrondir est également plus judicieux que le plafond. Alors:

- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset
{
    NSInteger page = round(proposedContentOffset.x / self.collectionView.bounds.size.height);
    return CGPointMake(page * self.collectionView.bounds.size.width, 0);
}
0
John Scalo

J'ai eu le problème avec mon projet, j'ai utilisé deux dispositions différentes pour UICollectionView.

mCustomCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"LandScapeCell" forIndexPath:indexPath];

theCustomCell *cell = [cv dequeueReusableCellWithReuseIdentifier:@"PortraitCell" forIndexPath:indexPath];

Ensuite, vérifiez-le pour chaque orientation et utilisez votre configuration pour chaque orientation.

0
Arun_
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
    CGSize pnt = CGSizeMake(70, 70);
    return pnt; }

-(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {

//    UIEdgeInsetsMake(<#CGFloat top#>, <#CGFloat left#>, <#CGFloat bottom#>, <#CGFloat right#>)
    return UIEdgeInsetsMake(3, 0, 3, 0); }

De cette façon, vous pouvez ajuster le contenu et la taille de votre cellule.

0
Nikhil Lihla

Vous souhaiterez peut-être masquer collectionView lors d'une animation (incorrecte) et afficher une vue fictive de la cellule qui effectue une rotation correcte à la place.

Pour une simple galerie de photos, j'ai trouvé un moyen de le faire qui a l'air bien. Voir ma réponse ici: Comment faire pivoter un UICollectionView similaire à l’application Photos et conserver la vue actuelle au centre?

0
Goodsquirrel

Utilisez <CollectionViewDelegateFlowLayout> et dans la méthode didRotateFromInterfaceOrientation: rechargez les données de CollectionView.

Implémentez la méthode collectionView:layout:sizeForItemAtIndexPath: de <CollectionViewDelegateFlowLayout> et, dans la méthode, vérifiez l'orientation de l'interface et appliquez votre taille personnalisée à chaque cellule.

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

    UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];

    if (UIInterfaceOrientationIsPortrait(orientation)) {

        return CGSizeMake(CGFloat width, CGFloat height);

    } else {

        return CGSizeMake(CGFloat width, CGFloat height);

    }

}
0
Soto_iGhost

J'ai un cas similaire dans lequel j'utilise ce

- (void)setFrame:(CGRect)frame
{
    CGFloat currentWidth = [self frame].size.width;
    CGFloat offsetModifier = [[self collectionView] contentOffset].x / currentWidth;

    [super setFrame:frame];

    CGFloat newWidth = [self frame].size.width;

    [[self collectionView] setContentOffset:CGPointMake(offsetModifier * newWidth, 0.0f) animated:NO];
}

Ceci est une vue qui contient une collectionView . Dans la vue d'ensemble, je le fais aussi.

- (void)setFrame:(CGRect)frame
{    
    UICollectionViewFlowLayout *collectionViewFlowLayout = (UICollectionViewFlowLayout *)[_collectionView collectionViewLayout];

    [collectionViewFlowLayout setItemSize:frame.size];

    [super setFrame:frame];
}

Cela permet d’ajuster la taille des cellules en mode plein écran (la vue complète doit être exacte;)). Si vous ne le faites pas ici, de nombreux messages d'erreur peuvent s'afficher pour indiquer que la taille de la cellule est plus grande que celle de la collection et que le comportement correspondant n'est pas défini et bla bla bla .....

Ces méthodes to peuvent bien sûr être fusionnées dans une sous-classe de la collectionview ou dans la vue contenant la collectionview, mais pour mon projet actuel, était-ce la voie logique à suivre.

0
Saren Inden

J'ai eu quelques problèmes avec le bloc animateAlongsideTransition dans animateAlongsideTransition (voir le code ci-dessous). 

Faites attention, elle est appelée pendant (mais pas avant) l'animation Ma tâche consistait à mettre à jour la position de défilement de la vue tableau en faisant défiler la rangée visible supérieure (j'ai rencontré le problème sur iPad lorsque les cellules de la vue tableau se sont déplacées lors de la rotation du dispositif, j’étais donc en train de trouver la solution à ce problème). Mais peut-être serait-il utile pour contentOffset également.

J'ai essayé de résoudre le problème de la manière suivante:

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

    __weak TVChannelsListTableViewController *weakSelf = self;

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        weakSelf.topVisibleRowIndexPath = [[weakSelf.tableView indexPathsForVisibleRows] firstObject];
    } completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        [weakSelf.tableView scrollToRowAtIndexPath:weakSelf.topVisibleRowIndexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
    }];
}

Mais ça n’a pas marché. Par exemple, le chemin d'index de la cellule du haut était (0, 20). Mais lorsque le bloc animateAlongsideTransition de rotation de périphérique a été appelé et que [[faibleSelf.tableView indexPathsForVisibleRows] firstObject] a renvoyé le chemin d’index (0, 27).

Je pensais que le problème était de récupérer les chemins d’index sur weakSelf. Par conséquent, pour résoudre le problème, j'ai déplacé self.topVisibleRowIndexPath avant l'appel de la méthode [coordinator animateAlongsideTransition: completion]:

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

    __weak TVChannelsListTableViewController *weakSelf = self;
    self.topVisibleRowIndexPath = [[weakSelf.tableView indexPathsForVisibleRows] firstObject];

    [coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext>  _Nonnull context) {
        [weakSelf.tableView scrollToRowAtIndexPath:weakSelf.topVisibleRowIndexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
    }];
}

Et l’autre chose intéressante que j’ai découverte est que les méthodes obsolètes willRotateToInterfaceOrientation et willRotateToInterfaceOrientation réussissent toujours à être appelées dans iOS version 8.0 ultérieure lorsque la méthode viewWillTransitionToSize n’est pas redéfinie. 

Donc, l’autre façon de résoudre le problème dans mon cas était d’utiliser une méthode obsolète au lieu d’une nouvelle. Je pense que ce ne serait pas la bonne solution, mais il est possible d’essayer si d’autres solutions ne fonctionnent pas :)

0
Yulia