UICollectionView aktueller Index der sichtbaren Zellen


115

Ich verwende zum UICollectionViewersten Mal in meiner iPad-Anwendung. Ich habe so eingestellt UICollectionView, dass Größe und Zellengröße gleich sind, dh nur einmal eine Zelle gleichzeitig angezeigt wird.

Problem: Wenn der Benutzer jetzt UICollectionView scrollt, muss ich wissen, welche Zelle sichtbar ist. Ich muss andere UI-Elemente bei Änderungen aktualisieren. Ich habe dafür keine Delegatenmethode gefunden. Wie kann ich das erreichen?

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

Was ich versucht habe:

Als UICollectionViewkonform zu UIScrollView, bekam ich, wenn der Benutzer- Bildlauf mit der UIScrollViewDelegateMethode endet

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

Aber wie kann ich innerhalb der obigen Funktion den aktuellen Index der sichtbaren Zellen von UICollectionView???


self.collectionViewFloors.indexPathsForVisibleItems
Aznix

Antworten:


130

Die Methode [collectionView sichtbare Zellen] gibt Ihnen alle gewünschten sichtbaren Zellen Array. Verwenden Sie es, wenn Sie erhalten möchten

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

Update auf Swift 5:

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

Können Sie bitte näher erläutern, woher self.mainImageCollection stammt? Vielen Dank im Voraus für Ihr weiteres Detail.
Benjamin McFerren

@BenjaminMcFerren Es ist die Sammlungsansicht, die Sie verwendet haben.
LE SANG

10
Die scrollViewDidScroll:Delegate-Methode bietet Aktualisierungen der Bildlaufansicht, wenn ein Bildlauf beendet ist (und ist hier wahrscheinlich die bessere Wahl). Im Gegensatz zur scrollViewDidEndDecelerating:Delegate-Methode, die nur aufgerufen wird, wenn die Bildlaufansicht " zum Stillstand kommt [von einem großen Bildlauf ]] " (siehe UIScrollView-Header).
Sam Spencer

1
IIRC ..DidScrollwird auch während einer kurzen Schriftrolle oft aufgerufen. Kann eine gute Sache sein oder auch nicht, je nachdem, was man tun möchte.
ToolmakerSteve

4
Seit iOS 10 ist es jetzt auch möglich, indexPathsForVisibleItems direkt zu verwenden :)
Ben

174

indexPathsForVisibleItemsfunktioniert möglicherweise in den meisten Situationen, gibt jedoch manchmal ein Array mit mehr als einem Indexpfad zurück und es kann schwierig sein, den gewünschten Pfad zu finden. In diesen Situationen können Sie Folgendes tun:

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

Dies funktioniert besonders gut, wenn jedes Element in Ihrer Sammlungsansicht den gesamten Bildschirm einnimmt.

Schnelle Version

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
Ich kann zustimmen, manchmal gibt indexPathsForVisibleItems mehr Zellen zurück, auch wenn wir der Meinung sind, dass es keinen solchen Fall geben sollte. Ihre Lösung funktioniert hervorragend.
MP23

2
Ich war total in dieser Art von Situation, nur ein Fix, der für mich funktionierte (aber mein Fall war mit Tabellenansicht), ich musste CGPointMake (CGRectGetMidX (sichtbareRect), CGRectGetMidY (sichtbareRect)) ändern; für CGPointMake (CGRectGetMidX (visibleRect), CGRectGetMinY (visibleRect));
JRafaelM

1
Ausgezeichnet, aber wenn die Zellen einen Abstand zwischen ihnen haben, dann wird visibleIndexPathmanchmal Null sein, alsoif (visibleIndexPath) == nil { let cells = collectionView.visibleCells() let visibleIndexPath = collectionView.indexPathForCell(cells[0] as! UICollectionViewCell)! as NSIndexPath }
Husam

Die einzige Lösung, die für meine Zellen in Vollbildgröße funktioniert hat. Swift-Version als Antwort unten hinzugefügt.
Sam Bing

1
Ich wünschte, ich könnte dies mehr unterstützen. Dieses Problem hat mich verrückt gemacht und diese Lösung hat den Tag gerettet.
SeanT

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

In Swift 2.2 kombinierte Arbeitsantworten:

 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)

    }

Wenn ich den Indexpfad zweimal bekomme, muss ich ihn nur einmal bekommen
Arshad Shaik

18

Der Vollständigkeit halber ist dies die Methode, die für mich funktioniert hat. Es war eine Kombination der Methoden von @Anthony & @ 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

Es ist wahrscheinlich am besten, UICollectionViewDelegate-Methoden zu verwenden: (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
In Bezug auf didEndDisplaying, von der Dokumentation: Teilt dem Delegierten , dass die angegebene Zelle aus der Sammlung Ansicht entfernt wurde. Verwenden Sie diese Methode, um zu erkennen, wann eine Zelle aus einer Sammlungsansicht entfernt wird, anstatt die Ansicht selbst zu überwachen, um festzustellen, wann sie verschwindet. Ich glaube also nicht, dass didEndDisplayingaufgerufen wird, wenn die Zelle angezeigt wird.
Jonny

9

Ich möchte nur für andere hinzufügen: Aus irgendeinem Grund habe ich die Zelle, die für den Benutzer sichtbar war, nicht erhalten, als ich mit pagingEnabled zur vorherigen Zelle in collectionView gescrollt habe.

Also füge ich den Code in dispatch_async ein, um ihm etwas "Luft" zu geben, und das funktioniert für mich.

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


            [visibleCell doSomthing];
        });
}

Das hat wirklich geholfen! Ich wusste nicht, dass ich den dispatch_async-Block verwenden kann, um ihn absolut fehlerfrei zu machen! Das ist die beste Antwort!
Kalafun

1
Für Swift 4: DispatchQueue.main.async {Wenn der Anruf von so etwas wiefunc tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
Jonny

Einige Klarstellungen: Dies hat mir auch geholfen, und ich hatte das gleiche / ähnliche Problem, als ich zurück zu einer geeigneten Ansichtszelle mit einer uicollectionview gescrollt habe. Der Aktualisierungsaufruf wurde willDisplay cellwie oben erwähnt initiiert . Ich denke, das Problem irgendwo in UIKit ist in meinem Fall wirklich dasselbe wie diese Antwort.
Jonny

7

Für 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

Einfachste Lösung, mit der Sie indexPath für sichtbare Zellen erhalten.

yourCollectionView.indexPathsForVisibleItems 

gibt das Array des Indexpfads zurück.

Nehmen Sie einfach das erste Objekt aus diesem Array.

yourCollectionView.indexPathsForVisibleItems.first

Ich denke, es sollte auch mit Objective - C gut funktionieren.


6

UICollectionView aktueller Index der sichtbaren Zellen: Swift 3

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

     return nil
}

Beste Antwort. Sauber und ein paar Zeilen.
Garanda

1
Perfekte Antwort .. Danke
Hardik Thakkar

3

Die Konvertierung von @ Anthonys Antwort auf Swift 3.0 hat für mich perfekt funktioniert:

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

Sie können scrollViewDidEndDecelerating: dafür verwenden

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

Das ist eine alte Frage, aber in meinem Fall ...

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

Überprüfen Sie auch dieses Snippet

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

0

In diesem Thread gibt es so viele Lösungen, die gut funktionieren, wenn die Zelle im Vollbildmodus angezeigt wird, aber die Grenzen der Sammlungsansicht und die Mittelpunkte von Visible Rect verwenden. Es gibt jedoch eine einfache Lösung für dieses Problem

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

Auf diese Weise können Sie den Indexpfad der sichtbaren Zelle abrufen. Ich habe DispatchQueue hinzugefügt, weil, wenn Sie schneller wischen und für einen kurzen Moment die nächste Zelle angezeigt wird, ohne dispactchQueue der Indexpfad der kurz angezeigten Zelle angezeigt wird, nicht die Zelle, die auf dem Bildschirm angezeigt wird.


-1

versuchen Sie dies, es funktioniert. (Im folgenden Beispiel habe ich zum Beispiel 3 Zellen.)

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

Wenn Sie die tatsächliche Anzahl anzeigen möchten, können Sie +1 hinzufügen

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.