UICollectionAfficher l'index de cellule visible actuel

115

J'utilise pour la UICollectionViewpremière fois dans mon application iPad. J'ai défini de UICollectionViewtelle sorte que sa taille et sa taille de cellule soient identiques, ce qui signifie qu'une seule cellule est affichée à la fois.

Problème: maintenant, lorsque l'utilisateur fait défiler UICollectionView, j'ai besoin de savoir quelle cellule est visible, je dois mettre à jour d'autres éléments de l'interface utilisateur en cas de modification. Je n'ai trouvé aucune méthode déléguée pour cela. Comment puis-je atteindre cet objectif?

Code:

[self.mainImageCollection setTag:MAIN_IMAGE_COLLECTION_VIEW];
[self.mainImageCollection registerClass:[InspirationMainImageCollectionCell class] forCellWithReuseIdentifier:@"cellIdentifier"];
[self.mainImageFlowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
[self.mainImageFlowLayout setMinimumInteritemSpacing:0.0f];
[self.mainImageFlowLayout setMinimumLineSpacing:0.0f];
self.mainImageFlowLayout.minimumLineSpacing = 0;
[self.mainImageCollection setPagingEnabled:YES];
[self.mainImageCollection setShowsHorizontalScrollIndicator:NO];
[self.mainImageCollection setCollectionViewLayout:self.mainImageFlowLayout];

Ce que j'ai essayé:

Comme UICollectionViewconforme à UIScrollView, j'ai obtenu lorsque le défilement de l'utilisateur se termine par la UIScrollViewDelegateméthode

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

Mais à l'intérieur de la fonction ci-dessus, comment puis-je obtenir l'indice de cellule visible actuel de UICollectionView???

Irfan DANOIS
la source
self.collectionViewFloors.indexPathsForVisibleItems
Aznix

Réponses:

130

La méthode [collectionView visibleCells] vous donne tous les tableaux visiblesCells que vous voulez. Utilisez-le lorsque vous voulez obtenir

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    for (UICollectionViewCell *cell in [self.mainImageCollection visibleCells]) {
        NSIndexPath *indexPath = [self.mainImageCollection indexPathForCell:cell];
        NSLog(@"%@",indexPath);
    }
}

Mettre à jour vers Swift 5:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    for cell in yourCollectionView.visibleCells {
        let indexPath = yourCollectionView.indexPath(for: cell)
        print(indexPath)
    }
}
LE SANG
la source
Pouvez-vous préciser d'où vient self.mainImageCollection? Merci d'avance pour vos détails supplémentaires.
Benjamin McFerren
@BenjaminMcFerren c'est la vue de collection que vous avez utilisée.
LE SANG
10
La scrollViewDidScroll:méthode déléguée fournit des mises à jour de la vue de défilement chaque fois qu'un défilement se termine (et est probablement un meilleur choix ici). Contrairement à la scrollViewDidEndDecelerating:méthode déléguée qui n'est appelée que lorsque la vue de défilement " s'arrête [à partir d'un grand défilement] " (voir l'en-tête UIScrollView).
Sam Spencer
1
IIRC, ..DidScrollest appelé plusieurs fois, même pendant un court défilement. Cela peut être une bonne chose ou non, selon ce que l'on veut faire.
ToolmakerSteve
4
Depuis iOS 10, il est désormais également possible d'utiliser directement indexPathsForVisibleItems :)
Ben
174

indexPathsForVisibleItemspeut fonctionner dans la plupart des situations, mais il renvoie parfois un tableau avec plus d'un chemin d'index et il peut être difficile de déterminer celui que vous voulez. Dans ces situations, vous pouvez faire quelque chose comme ceci:

CGRect visibleRect = (CGRect){.origin = self.collectionView.contentOffset, .size = self.collectionView.bounds.size};
CGPoint visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect));
NSIndexPath *visibleIndexPath = [self.collectionView indexPathForItemAtPoint:visiblePoint];

Cela fonctionne particulièrement bien lorsque chaque élément de votre vue de collection occupe tout l'écran.

Version Swift

let visibleRect = CGRect(origin: collectionView.contentOffset, size: collectionView.bounds.size)
let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
let visibleIndexPath = collectionView.indexPathForItem(at: visiblePoint)
lobianco
la source
9
Je peux être d'accord, parfois indexPathsForVisibleItems renvoie plus de cellules, même si nous pensons qu'il ne devrait pas y avoir un tel cas. Votre solution fonctionne très bien.
MP23
2
J'étais totalement sur ce genre de situation, juste un correctif qui a fonctionné pour moi (mais mon cas était avec tableview), j'avais besoin de changer le CGPointMake (CGRectGetMidX (visibleRect), CGRectGetMidY (visibleRect)); pour CGPointMake (CGRectGetMidX (visibleRect), CGRectGetMinY (visibleRect));
JRafaelM
1
Excellent, mais si les cellules ont un espace entre elles, elles visibleIndexPathseront parfois nulles, alorsif (visibleIndexPath) == nil { let cells = collectionView.visibleCells() let visibleIndexPath = collectionView.indexPathForCell(cells[0] as! UICollectionViewCell)! as NSIndexPath }
Husam
La seule solution qui a fonctionné pour mes cellules de taille plein écran. Ajout de la version Swift comme réponse ci-dessous.
Sam Bing
1
J'aurais aimé pouvoir voter davantage. Ce problème m'a rendu fou et cette solution a sauvé la mise.
SeanT le
77

Swift 5 :

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    var visibleRect = CGRect()

    visibleRect.origin = collectionView.contentOffset
    visibleRect.size = collectionView.bounds.size

    let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)

    guard let indexPath = collectionView.indexPathForItem(at: visiblePoint) else { return } 

    print(indexPath)
}

Réponses pratiques combinées dans Swift 2.2:

 func scrollViewDidEndDecelerating(scrollView: UIScrollView) {

        var visibleRect = CGRect()

        visibleRect.origin = self.collectionView.contentOffset
        visibleRect.size = self.collectionView.bounds.size

        let visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect))

        let visibleIndexPath: NSIndexPath = self.collectionView.indexPathForItemAtPoint(visiblePoint)

        guard let indexPath = visibleIndexPath else { return } 
        print(indexPath)

    }
Sam Bing
la source
Obtenir indexpath deux fois, je n'ai besoin de l'obtenir qu'une seule fois
Arshad Shaik
18

Par souci d'exhaustivité, c'est la méthode qui a fini par fonctionner pour moi. C'était une combinaison des méthodes de @Anthony et @ iAn.

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
      CGRect visibleRect = (CGRect){.origin = self.collectionView.contentOffset, .size = self.collectionView.bounds.size};
      CGPoint visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect));
      NSIndexPath *visibleIndexPath = [self.collectionView indexPathForItemAtPoint:visiblePoint];
      NSLog(@"%@",visibleIndexPath);
}
Vincil Bishop
la source
11

Il sera probablement préférable d'utiliser les méthodes UICollectionViewDelegate: (Swift 3)

// Called before the cell is displayed    
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    print(indexPath.row)
}

// Called when the cell is displayed
func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    print(indexPath.row)
}
Monsieur Stanev
la source
3
Concernant didEndDisplaying, à partir de la documentation: indique au délégué que la cellule spécifiée a été supprimée de la vue de collection. Utilisez cette méthode pour détecter lorsqu'une cellule est supprimée d'une vue de collection, au lieu de surveiller la vue elle-même pour voir quand elle disparaît. Donc je ne pense pas didEndDisplayingqu'on appelle quand la cellule est affichée.
Jonny
9

Je veux juste ajouter pour les autres: pour une raison quelconque, je n'ai pas obtenu la cellule qui était visible pour l'utilisateur lorsque je faisais défiler la cellule précédente dans collectionView avec pagingEnabled.

J'insère donc le code dans dispatch_async pour lui donner un peu d '"air" et cela fonctionne pour moi.

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    dispatch_async(dispatch_get_main_queue(), ^{
            UICollectionViewCell * visibleCell= [[self.collectionView visibleCells] objectAtIndex:0];


            [visibleCell doSomthing];
        });
}
user1105951
la source
Cela a vraiment aidé! Je n'avais pas réalisé que je pouvais utiliser le bloc dispatch_async pour le rendre absolument parfait! C'est la meilleure réponse!
kalafun
1
Pour Swift 4: DispatchQueue.main.async {lorsque l'appel commence à partir de quelque chose commefunc tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
Jonny
Quelques précisions: cela m'a aidé aussi, et j'ai eu le même problème / similaire, en revenant à uitableviewcell contenant une uicollectionview. L'appel de rafraîchissement a été lancé willDisplay cellcomme mentionné ci-dessus Je pense que le problème, quelque part dans UIKit, est vraiment le même dans mon cas que cette réponse.
Jonny
7

Pour Swift 3.0

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let visibleRect = CGRect(origin: colView.contentOffset, size: colView.bounds.size)
    let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
    let indexPath = colView.indexPathForItem(at: visiblePoint)
}
Hiren Panchal
la source
6

Swift 3.0

Solution la plus simple qui vous donnera indexPath pour les cellules visibles.

yourCollectionView.indexPathsForVisibleItems 

renverra le tableau de indexpath.

Prenez simplement le premier objet du tableau comme celui-ci.

yourCollectionView.indexPathsForVisibleItems.first

Je suppose que cela devrait également fonctionner avec Objective-C.

Anil Kukadeja
la source
6

UICollection Afficher l'index de cellule visible actuel: Swift 3

var visibleCurrentCellIndexPath: IndexPath? {
    for cell in self.collectionView.visibleCells {
        let indexPath = self.collectionView.indexPath(for: cell)
        return indexPath
     }

     return nil
}
Mihail Salari
la source
Meilleure réponse. Propre et quelques lignes.
garanda
1
Réponse parfaite .. Merci
Hardik Thakkar
3

la conversion de la réponse de @ Anthony en Swift 3.0 a parfaitement fonctionné pour moi:

func scrollViewDidScroll(_ scrollView: UIScrollView) {

    var visibleRect = CGRect()
    visibleRect.origin = yourCollectionView.contentOffset
    visibleRect.size = yourCollectionView.bounds.size
    let visiblePoint = CGPoint(x: CGFloat(visibleRect.midX), y: CGFloat(visibleRect.midY))
    let visibleIndexPath: IndexPath? = yourCollectionView.indexPathForItem(at: visiblePoint)
    print("Visible cell's index is : \(visibleIndexPath?.row)!")
}

la source
2

Vous pouvez utiliser scrollViewDidEndDecelerating: pour cela

//@property (strong, nonatomic) IBOutlet UICollectionView *collectionView;

   - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{

        for (UICollectionViewCell *cell in [self.collectionView visibleCells]) {
            NSIndexPath *indexPath = [self.collectionView indexPathForCell:cell];
            NSUInteger lastIndex = [indexPath indexAtPosition:[indexPath length] - 1];
            NSLog(@"visible cell value %d",lastIndex);
        }

    }
Raaz
la source
0

C'est une vieille question mais dans mon cas ...

- (void) scrollViewWillBeginDragging:(UIScrollView *)scrollView {

    _m_offsetIdx = [m_cv indexPathForCell:m_cv.visibleCells.firstObject].row;
}

- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    _m_offsetIdx = [m_cv indexPathForCell:m_cv.visibleCells.lastObject].row;
}
Hwangho Kim
la source
0

Vérifiez également cet extrait

let isCellVisible = collectionView.visibleCells.map { collectionView.indexPath(for: $0) }.contains(inspectingIndexPath)
Nik Kov
la source
0

Dans ce fil, il y a tellement de solutions qui fonctionnent bien si la cellule prend le plein écran mais elles utilisent les limites de vue de collection et les points médians de Visible rect Cependant il existe une solution simple à ce problème

    DispatchQueue.main.async {
        let visibleCell = self.collImages.visibleCells.first
        print(self.collImages.indexPath(for: visibleCell))
    }

par cela, vous pouvez obtenir indexPath de la cellule visible. J'ai ajouté DispatchQueue car lorsque vous glissez plus rapidement et si pendant un bref instant la cellule suivante est affichée, sans dispactchQueue, vous obtiendrez indexPath de la cellule brièvement affichée et non de la cellule affichée à l'écran.

kuldip
la source
-1

essayez ceci, cela fonctionne. (dans l'exemple ci-dessous, j'ai 3 cellules par exemple.)

    func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
    let visibleRect = CGRect(origin: self.collectionView.contentOffset, size: self.collectionView.bounds.size)
    let visiblePoint = CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect))
    let visibleIndexPath = self.collectionView.indexPathForItemAtPoint(visiblePoint)
    if let v = visibleIndexPath {
        switch v.item {
        case 0: setImageDescription()
            break
        case 1: setImageConditions()
            break
        case 2: setImageResults()
            break
        default: break
        }
    }
Arash Jamshidi
la source
-2

Swift 3 et Swift 4:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
   var visibleRect = CGRect()

   visibleRect.origin = collectionView.contentOffset
   visibleRect.size = collectionView.bounds.size

   let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)

   guard let indexPath = collectionView.indexPathForItem(at: visiblePoint) else { return } 

   print(indexPath[1])
}

Si vous souhaitez afficher le nombre réel, vous pouvez ajouter +1

Jamil Hasnine Tamim
la source