UICollection Visualizza l'indice di cella visibile corrente


115

sto usando UICollectionView prima volta nella mia applicazione per iPad. Ho impostato in modo UICollectionViewtale che le sue dimensioni e le dimensioni della cella siano le stesse, significa solo una volta che la cella viene visualizzata alla volta.

Problema: ora, quando l'utente scorre UICollectionView, ho bisogno di sapere quale cella è visibile, devo aggiornare altri elementi dell'interfaccia utente al momento della modifica. Non ho trovato alcun metodo delegato per questo. Come posso raggiungere questo obiettivo?

Codice:

[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];

Quello che ho provato:

Come UICollectionViewconforme a UIScrollView, ho ottenuto quando lo scorrimento dell'utente termina con il UIScrollViewDelegatemetodo

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

Ma all'interno della funzione sopra come posso ottenere l'indice di cella visibile corrente di UICollectionView???


self.collectionViewFloors.indexPathsForVisibleItems
Aznix

Risposte:


130

Il metodo [collectionView visibleCells] ti fornisce tutti gli array visibleCells che desideri. Usalo quando vuoi ottenere

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

Aggiornamento a Swift 5:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    for cell in yourCollectionView.visibleCells {
        let indexPath = yourCollectionView.indexPath(for: cell)
        print(indexPath)
    }
}

Puoi spiegare da dove proviene self.mainImageCollection? Molte grazie in anticipo per i tuoi ulteriori dettagli.
Benjamin McFerren

@BenjaminMcFerren è la collectionview che hai usato.
LE SANG

10
Il scrollViewDidScroll:metodo delegato fornisce aggiornamenti della visualizzazione a scorrimento ogni volta che termina uno scorrimento (ed è probabilmente una scelta migliore qui). Al contrario del scrollViewDidEndDecelerating:metodo delegato che viene chiamato solo quando la visualizzazione a scorrimento "si arresta [da un grande scorrimento] " (vedere intestazione UIScrollView).
Sam Spencer

1
IIRC, ..DidScrollviene chiamato molte volte, anche durante un breve scorrimento. Può o non può essere una buona cosa, a seconda di ciò che si vuole fare.
ToolmakerSteve

4
Da iOS 10 ora è anche possibile utilizzare indexPathsForVisibleItems direttamente :)
Ben

174

indexPathsForVisibleItemspotrebbe funzionare per la maggior parte delle situazioni, ma a volte restituisce un array con più di un percorso di indice e può essere complicato capire quello che desideri. In quelle situazioni, puoi fare qualcosa del genere:

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

Funziona particolarmente bene quando ogni elemento nella visualizzazione della raccolta occupa l'intero schermo.

Versione rapida

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
Sono d'accordo, a volte indexPathsForVisibleItems restituisce più celle, anche se pensiamo che non dovrebbe esserci un caso del genere. La tua soluzione funziona alla grande.
MP23

2
Ero totalmente su questo tipo di situazione, solo una soluzione che ha funzionato per me (ma il mio caso era con tableview), avevo bisogno di cambiare CGPointMake (CGRectGetMidX (visibleRect), CGRectGetMidY (visibleRect)); per CGPointMake (CGRectGetMidX (visibleRect), CGRectGetMinY (visibleRect));
JRafaelM

1
Eccellente, ma se le celle hanno uno spazio tra di loro, a visibleIndexPathvolte sarà zero, quindiif (visibleIndexPath) == nil { let cells = collectionView.visibleCells() let visibleIndexPath = collectionView.indexPathForCell(cells[0] as! UICollectionViewCell)! as NSIndexPath }
Husam

L'unica soluzione che ha funzionato per le mie celle a schermo intero. Aggiunta la versione Swift come risposta di seguito.
Sam Bing

1
Vorrei poter votare di più. Questo problema mi ha fatto impazzire e questa soluzione ha salvato la giornata.
Seduto il

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

Risposte di lavoro combinate in 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)

    }

Ottenendo indexpath due volte, ho bisogno di ottenere solo una volta
Arshad Shaik

18

Per completezza, questo è il metodo che ha finito per funzionare per me. Era una combinazione dei metodi di @Anthony e @ 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

Probabilmente sarà meglio usare i metodi 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
Per quanto riguarda didEndDisplaying, dalla documentazione: indica al delegato che la cella specificata è stata rimossa dalla visualizzazione della raccolta. Utilizzare questo metodo per rilevare quando una cella viene rimossa da una visualizzazione raccolta, invece di monitorare la visualizzazione stessa per vedere quando scompare. Quindi non credo didEndDisplayingvenga chiamato quando viene visualizzata la cella.
Jonny

9

Voglio solo aggiungere per altri: per qualche motivo, non ho ottenuto la cella che era visibile all'utente quando stavo scorrendo alla cella precedente in collectionView con pagingEnabled.

Quindi inserisco il codice all'interno di dispatch_async per dargli un po 'di "aria" e questo funziona per me.

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


            [visibleCell doSomthing];
        });
}

Questo ha davvero aiutato! Non mi sono reso conto di poter utilizzare il blocco dispatch_async per renderlo assolutamente impeccabile! Questa è la migliore risposta!
kalafun

1
Per Swift 4: DispatchQueue.main.async {quando la chiamata inizia da qualcosa comefunc tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
Jonny

Qualche chiarimento: anche questo mi ha aiutato, e ho avuto lo stesso / simile problema, quando tornavo a uitableviewcell contenente una uicollectionview. La chiamata di aggiornamento iniziata da willDisplay cellcome menzionato sopra. Penso che il problema, da qualche parte in UIKit, sia davvero lo stesso nel mio caso di questa risposta.
Jonny

7

Per 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

La soluzione più semplice che ti darà indexPath per le celle visibili ..

yourCollectionView.indexPathsForVisibleItems 

restituirà l'array di indexpath.

Prendi il primo oggetto da un array in questo modo.

yourCollectionView.indexPathsForVisibleItems.first

Immagino che dovrebbe funzionare bene anche con Objective - C.


6

UICollection Visualizza l'indice di cella visibile corrente: Swift 3

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

     return nil
}

Migliore risposta. Pulito e poche righe.
Garanda

1
Risposta perfetta .. Grazie
Hardik Thakkar

3

convertire la risposta di @ Anthony a Swift 3.0 ha funzionato perfettamente per me:

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

Puoi usare scrollViewDidEndDecelerating: per questo

//@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

Questa è una vecchia domanda ma nel mio caso ...

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

Controlla anche questo snippet

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

0

In questo thread, ci sono così tante soluzioni che funzionano bene se la cella occupa lo schermo intero ma usano i limiti della vista raccolta e i punti medi di Visible rect Tuttavia esiste una soluzione semplice a questo problema

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

da questo, puoi ottenere indexPath della cella visibile. Ho aggiunto DispatchQueue perché quando scorri più velocemente e se per un breve momento viene mostrata la cella successiva, senza dispactchQueue otterrai indexPath della cella mostrata brevemente non la cella che viene visualizzata sullo schermo.


-1

prova questo, funziona. (Nell'esempio sotto ho 3 celle per esempio.)

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

Se vuoi mostrare il numero effettivo, puoi aggiungere +1

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.