web-dev-qa-db-fra.com

UICollectionView - Redimensionnement des cellules sur une rotation de périphérique - Swift

J'ai créé un UICollectionView afin de pouvoir organiser les vues dans des colonnes ordonnées. J'aimerais qu'il y ait une seule colonne sur les périphériques de plus de 500 pixels de large.

Pour ce faire, j'ai créé cette fonction:

func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    let size = collectionView.frame.width
    if (size > 500) {
        return CGSize(width: (size/2) - 8, height: (size/2) - 8)
    }
    return CGSize(width: size, height: size)
}

Cela fonctionne comme prévu lors du premier chargement, mais lorsque je fais pivoter le périphérique, le calcul ne se reproduit pas toujours et les vues ne sont pas toujours redessinées comme prévu. Voici mon code pour la rotation de l'appareil:

override func willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
    collectionView.collectionViewLayout.invalidateLayout()
    self.view.setNeedsDisplay()
}

Je suppose que j'ai oublié de redessiner quelque chose, mais je ne sais pas quoi. Toutes les idées sont très reconnaissantes!

41
Ben

Vous pouvez utiliser viewWillLayoutSubviews. Cette question devrait être utile, mais elle est appelée de manière bassiste chaque fois que la vue du contrôleur de vue est sur le point de disposer ses sous-vues. 

Votre code ressemblera à ceci:

override func viewWillLayoutSubviews() {
  super.viewWillLayoutSubviews()

  guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
    return
  }

  if UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation) {
    //here you can do the logic for the cell size if phone is in landscape
  } else {
    //logic if not landscape 
  }

  flowLayout.invalidateLayout()
}
19
adolfosrs

Le moyen le plus simple de le faire est peut-être invalidateLayout pendant le viewWillTransitionToSize:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    super.viewWillTransition(to: size, with: coordinator)
    guard let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout else {
        return
    }
    flowLayout.invalidateLayout()
}
57
valvoline

J'ai eu tendance à utiliser viewWillTransitionToSize avec la même chose et à simplement appeler invalidateLayout()

13
thesundayscientist

La méthode traitCollectionDidChange peut également être utilisée à la place de viewWillLayoutSubviews:

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)

    guard let previousTraitCollection = previousTraitCollection, traitCollection.verticalSizeClass != previousTraitCollection.verticalSizeClass ||
        traitCollection.horizontalSizeClass != previousTraitCollection.horizontalSizeClass else {
            return
    }

    if traitCollection.horizontalSizeClass == .regular && traitCollection.verticalSizeClass == .regular {
        // iPad portrait and landscape
        // do something here...
    }
    if traitCollection.horizontalSizeClass == .compact && traitCollection.verticalSizeClass == .regular {
        // iPhone portrait
        // do something here...
    }
    if traitCollection.horizontalSizeClass == .regular && traitCollection.verticalSizeClass = .compact {
        // iPhone landscape
        // do something here...
    }
    collectionView?.collectionViewLayout.invalidateLayout()
    collectionView?.reloadData()
}
2
birdy

Pour que la vue de collection redimensionne ses cellules et anime le changement au cours de la rotation, utilisez le coordinateur de transition:

(Swift 4+)

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

    // Have the collection view re-layout its cells.
    coordinator.animate(
        alongsideTransition: { _ in self.collectionView.collectionViewLayout.invalidateLayout() },
        completion: { _ in }
    )
}
1
Graham Perks

J'avais pour tâche d'ajuster la taille des éléments dans la sous-classe UICollectionView. La meilleure méthode pour cela est setFrame. Propriété collectionViewFlowLayout J'ai passé de ViewController (dans mon cas, c'était la sortie de la disposition de flux par défaut).

// .h
@property (nonatomic, weak) UICollectionViewFlowLayout *collectionViewFlowLayout;

// .m
- (void)setFrame:(CGRect)frame {
    if (!CGSizeEqualToSize(frame.size, self.frame.size)) {
        collectionViewFlowLayout.itemSize =
        CGSizeMake((UIDeviceOrientationIsLandscape(UIDevice.currentDevice.orientation) ? (frame.size.width - 10) / 2 : frame.size.width), collectionViewFlowLayout.itemSize.height);
    }
    [super setFrame:frame];
}
0
Igor

Vous pouvez créer un contenu de masque et le déplacer vers la collectionView. Une fois l'animation paysage/portrait terminée, vous devez la supprimer dès que possible.

Voici un exemple:

@property (strong, nonatomic) UIImageView *maskImage;

.........

- (UIImageView *) imageForCellAtIndex: (NSInteger) index {
    UICollectionView *collectionView = self.pagerView.test;
    FSPagerViewCell *cell = nil;
    NSArray *indexPaths = [collectionView indexPathsForVisibleItems];
    for (NSIndexPath *indexPath in indexPaths) {
        if (indexPath.item == index) {
            cell = (FSPagerViewCell *)[collectionView cellForItemAtIndexPath: indexPath];
            break;
        }
    }
    if (cell) {
        return cell.imageView;
    }
    return nil;
}


- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator

{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context)
     {
         UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
         [self test_didRotateFromInterfaceOrientation: orientation];

         UIImageView *imageView = [self imageForCellAtIndex: self.pagerView.currentIndex];
         if (imageView) {
                 UIImageView *imageView = [self imageForCellAtIndex: self.pagerView.currentIndex];
                 CGSize itemSize = self.pagerView.itemSize;
                 UIImageView *newImage = [[UIImageView alloc] initWithImage: imageView.image];
                 [newImage setFrame: CGRectMake((_contentView.bounds.size.width - itemSize.width)/2.0f, 0, itemSize.width, itemSize.height)];
                 newImage.contentMode = imageView.contentMode;
                 newImage.clipsToBounds = imageView.clipsToBounds;
                 newImage.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
                 [self.pagerView addSubview: newImage];

                 self.maskImage = newImage;
         }

         [self.pagerView.test performBatchUpdates:^{
             [self.pagerView.test setCollectionViewLayout:self.pagerView.test.collectionViewLayout animated:YES];
         } completion:nil];
         // do whatever
     } completion:^(id<UIViewControllerTransitionCoordinatorContext> context)
     {
         [self.maskImage removeFromSuperview];
     }];
}
0
KuTeo