UICollectionView défilement automatique jusqu'à la cellule à IndexPath

101

Avant de charger la vue de collection, l'utilisateur définit le nombre d'images dans le tableau de la vue de collection. Toutes les cellules ne tiennent pas sur l'écran. J'ai 30 cellules et seulement 6 à l'écran.

La question: Comment faire défiler jusqu'à la cellule avec l'image souhaitée automatiquement à la charge de UICollectionView?

AlexanderZ
la source

Réponses:

90

J'ai trouvé que le défilement viewWillAppearpeut ne pas fonctionner de manière fiable car la vue de collection n'a pas encore terminé sa mise en page; vous pouvez faire défiler jusqu'au mauvais élément.

J'ai également constaté que le défilement viewDidAppearprovoquera un flash momentané de la vue non défilée pour être visible.

Et, si vous faites défiler à chaque fois viewDidLayoutSubviews, l'utilisateur ne pourra pas faire défiler manuellement car certaines mises en page de collection provoquent une mise en page de sous-vue chaque fois que vous faites défiler.

Voici ce que j'ai trouvé fonctionne de manière fiable:

Objectif c :

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    // If we haven't done the initial scroll, do it once.
    if (!self.initialScrollDone) {
        self.initialScrollDone = YES;

        [self.collectionView scrollToItemAtIndexPath:self.myInitialIndexPath
                                    atScrollPosition:UICollectionViewScrollPositionRight animated:NO];
    }
}

Rapide :

 override func viewWillLayoutSubviews() {

    super.viewWillLayoutSubviews()

      if (!self.initialScrollDone) {

         self.initialScrollDone = true
         self.testNameCollectionView.scrollToItem(at:selectedIndexPath, at: .centeredHorizontally, animated: true)
    }

}
LenK
la source
Bon conseil. Je vais insister sur l'utilisation d'un initialScrollDonedrapeau comme LenK l'a fait puisque cette méthode sera appelée plus d'une fois et si vous appelez scrollToItemAtIndexPath: plus d'une fois, il semble rendre la collectionView unscrollable (simulateur iOS iPhone 5 / iOS 8)
maz
1
Fonctionne très bien dans iOS8. Devrait être la réponse acceptée.
Nick
5
Cela n'a pas fonctionné pour moi dans iOS 8.3. J'ai dû appeler [self.collectionView layoutIfNeeded];avant, ce qui fonctionne également lorsque vous faites cela à l'intérieur viewDidLoad.
Brent Dillingham
1
Cela devrait certainement être la réponse acceptée. La réponse ci-dessus m'a donné un flash car la première cellule a été affichée, puis mise à jour avec ma nouvelle cellule, cette méthode se transforme de manière transparente.
simon_smiley
cela fonctionne également sur iOS 9 et ne gâche pas non plus ma disposition automatique.
M. Zystem
168

Nouvelle réponse modifiée:

Ajouter ceci dans viewDidLayoutSubviews

RAPIDE

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    let indexPath = IndexPath(item: 12, section: 0)
    self.collectionView.scrollToItem(at: indexPath, at: [.centeredVertically, .centeredHorizontally], animated: true)
}

Normalement, .centerVerticallyc'est le cas

ObjC

-(void)viewDidLayoutSubviews {
   [super viewDidLayoutSubviews];
    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:12 inSection:0];
   [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically | UICollectionViewScrollPositionCenteredHorizontally animated:NO];
}

Ancienne réponse fonctionnant pour l'ancien iOS

Ajoutez ceci dans viewWillAppear:

[self.view layoutIfNeeded];
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO];
Boweidmann
la source
4
C'est exactement ce que j'essaie d'utiliser. J'ai le numéro de l'objet dans le tableau et je connais le nom de cet objet. Mais je ne sais pas comment mettre indexPath correct ... Des idées?
AlexanderZ
4
Vous pouvez facilement créer indexPath:[NSIndexPath indexPathForItem:numberOfTheObject inSection:sectionNumber]
boweidmann
3
Eu quelques erreurs. Mais ensuite, j'ai mis tout le code mentionné dans (void) viewDidLayoutSubviews {} et maintenant cela fonctionne très bien. Thanx Bogdan.
AlexanderZ
1
Comment puis-je «zoomer» sur la cellule et l'afficher agrandie?
fatuhoku
9
Cela ne fonctionne pas de manière fiable sur iOS 7.1 et peut faire apparaître la vue de la collection sous la forme d'un écran noir. Je ne voulais pas non plus conserver l'état comme la solution dans viewDidLayoutSubviews ci-dessous. Ce que j'ai fait, c'est simplement effectué un [self.view layoutIfNeeded] juste avant l'appel à scrollToItemAtIndexPath
Daniel Galasko
15

Je suis totalement d'accord avec la réponse ci-dessus. La seule chose est que pour moi, la solution a été de définir le code dans viewDidAppear

viewDidAppear 
{
    [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES];
}

Lorsque j'ai utilisé le viewWillAppear, le défilement ne fonctionnait pas.

Raistlin
la source
2
viewDidAppear a l'inconvénient de voir la vue de la collection bouger / clignoter, la vue est déjà apparue (comme son nom l'indique) ... Je me demande vraiment pourquoi Apple n'a pas conçu ce cas pas si inhabituel d'une manière plus agréable, la manière viewDidLayoutSubviews est un peu maladroit
TheEye
12

cela a semblé fonctionner pour moi, c'est similaire à une autre réponse mais a quelques différences distinctes:

- (void)viewDidLayoutSubviews
{     
   [self.collectionView layoutIfNeeded];
   NSArray *visibleItems = [self.collectionView indexPathsForVisibleItems];
   NSIndexPath *currentItem = [visibleItems objectAtIndex:0];
   NSIndexPath *nextItem = [NSIndexPath indexPathForItem:someInt inSection:currentItem.section];

   [self.collectionView scrollToItemAtIndexPath:nextItem atScrollPosition:UICollectionViewScrollPositionNone animated:YES];
}
serre
la source
12

Rapide:

your_CollectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: UICollectionViewScrollPosition.CenteredHorizontally, animated: true)

Swift 3

let indexPath = IndexPath(row: itemIndex, section: sectionIndex)

collectionView.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition.right, animated: true)

Position de défilement:

UICollectionViewScrollPosition.CenteredHorizontally / UICollectionViewScrollPosition.CenteredVertically
Rajesh Loganathan
la source
9

Vous pouvez utiliser GCD pour distribuer le défilement dans l'itération suivante de la boucle d'exécution principale dans viewDidLoad pour obtenir ce comportement. Le défilement sera effectué avant l'affichage de la vue de la collection à l'écran, il n'y aura donc pas de clignotement.

- (void)viewDidLoad {
    dispatch_async (dispatch_get_main_queue (), ^{
        NSIndexPath *indexPath = YOUR_DESIRED_INDEXPATH;
        [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
    });
}
Tianyu Wang
la source
comment obtenir indexpath lorsque la position est 3?
kemdo
5

Je recommanderais de le faire dans collectionView: willDisplay:Ensuite, vous pouvez être sûr que le délégué de vue de collection fournit quelque chose. Voici mon exemple:

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    /// this is done to set the index on start to 1 to have at index 0 the last image to have a infinity loop
    if !didScrollToSecondIndex {
        self.scrollToItem(at: IndexPath(row: 1, section: 0), at: .left, animated: false)
        didScrollToSecondIndex = true
    }
}
Eike
la source
1
J'ai trouvé que l'utilisation de didEndDisplaying, au lieu de willDisplay, était le moyen de faire fonctionner correctement cela. willDisplay fonctionnait tant que la cellule était à l'écran; cependant, si la cellule n'était pas encore à l'écran, cette méthode ne me faisait pas défiler jusqu'à elle. didEndDisplaying fonctionnait parfaitement pour ce cas d'utilisation. Appréciez votre réponse qui me met sur la bonne voie!
C6Silver
2

Comme alternative à mentionné ci-dessus. Appel après le chargement des données:

Rapide

collectionView.reloadData()
collectionView.layoutIfNeeded()
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .right)
nCod3d
la source
1

Toutes les réponses ici - hacks. J'ai trouvé un meilleur moyen de faire défiler la vue de la collection quelque part après relaodData:
disposition de la vue de la collection de sous - classes quelle que soit la disposition que vous utilisez, remplacez la méthode prepareLayout, après le super appel, définissez le décalage dont vous avez besoin.
ex: https://stackoverflow.com/a/34192787/1400119

trickster77777
la source