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???


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)
    }
}

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)

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)

    }

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);
}

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)
}

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

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)
}

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.


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
}

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)!")
}

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);
        }

    }

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

0

Vérifiez également cet extrait

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

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.


-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
        }
    }

-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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.