UICollectionView bieżący widoczny indeks komórek

115

Po UICollectionViewraz pierwszy używam aplikacji na iPada. Ustawiłem UICollectionViewtak, aby jego rozmiar i rozmiar komórki były takie same, czyli tylko raz komórka jest wyświetlana na raz.

Problem: Teraz, gdy użytkownik przewija UICollectionView, muszę wiedzieć, która komórka jest widoczna, muszę zaktualizować inne elementy interfejsu użytkownika po zmianie. Nie znalazłem do tego żadnej metody delegata. Jak mogę to osiągnąć?

Kod:

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

Co próbowałem:

Zgodnie UICollectionViewz tym UIScrollView, otrzymałem, gdy przewijanie użytkownika kończy się UIScrollViewDelegatemetodą

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

Ale wewnątrz powyższej funkcji, jak mogę uzyskać bieżący widoczny indeks komórki UICollectionView???

Irfan DUŃSKI
źródło
self.collectionViewFloors.indexPathsForVisibleItems
Aznix

Odpowiedzi:

130

Metoda [collectionView visibleCells] daje ci wszystkie potrzebne tablice visibleCells. Użyj go, kiedy chcesz

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

Zaktualizuj do Swift 5:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    for cell in yourCollectionView.visibleCells {
        let indexPath = yourCollectionView.indexPath(for: cell)
        print(indexPath)
    }
}
LE SANG
źródło
Czy możesz wyjaśnić, skąd pochodzi self.mainImageCollection? Z góry dziękujemy za dalsze szczegóły.
Benjamin McFerren
@BenjaminMcFerren to widok kolekcji, którego użyłeś.
LE SANG
10
Metoda scrollViewDidScroll:delegata zapewnia aktualizacje widoku przewijania po zakończeniu przewijania (i prawdopodobnie jest tutaj lepszym wyborem). W przeciwieństwie do scrollViewDidEndDecelerating:metody delegata, która jest wywoływana tylko wtedy, gdy widok przewijania „ grinduje do zatrzymania [z dużego przewijania] ” (patrz nagłówek UIScrollView).
Sam Spencer,
1
IIRC, ..DidScrolljest wywoływany wiele razy, nawet podczas krótkiego przewijania. Może, ale nie musi, być dobrą rzeczą, w zależności od tego, co chce się robić.
ToolmakerSteve
4
Od iOS 10 można teraz również bezpośrednio używać indexPathsForVisibleItems :)
Ben
174

indexPathsForVisibleItemsmoże działać w większości sytuacji, ale czasami zwraca tablicę z więcej niż jedną ścieżką indeksu i może być trudne znalezienie tego, który chcesz. W takich sytuacjach możesz zrobić coś takiego:

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

Działa to szczególnie dobrze, gdy każdy element w widoku kolekcji zajmuje cały ekran.

Szybka wersja

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
źródło
9
Zgadzam się, czasami indexPathsForVisibleItems zwraca więcej komórek, nawet jeśli uważamy, że nie powinno być takiego przypadku. Twoje rozwiązanie działa świetnie.
MP23
2
Byłem całkowicie w tego rodzaju sytuacji, tylko jedna poprawka, która działała dla mnie (ale mój przypadek dotyczył widoku tabeli), musiałem zmienić CGPointMake (CGRectGetMidX (visibleRect), CGRectGetMidY (visibleRect)); dla CGPointMake (CGRectGetMidX (visibleRect), CGRectGetMinY (visibleRect));
JRafaelM
1
Doskonale, ale jeśli komórki mają odstępy między nimi, visibleIndexPathczasami będzie zero, więcif (visibleIndexPath) == nil { let cells = collectionView.visibleCells() let visibleIndexPath = collectionView.indexPathForCell(cells[0] as! UICollectionViewCell)! as NSIndexPath }
Husam
Jedyne rozwiązanie, które działało dla moich komórek o pełnym rozmiarze ekranu. Dodano wersję Swift jako odpowiedź poniżej.
Sam Bing,
1
Chciałbym móc to bardziej głosować. Ten problem doprowadza mnie do szału i to rozwiązanie uratowało dzień.
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)
}

Odpowiedzi robocze połączone w 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
źródło
Uzyskując ścieżkę indeksu dwa razy, potrzebuję tylko raz
Arshad Shaik
18

Ze względu na kompletność ta metoda okazała się dla mnie skuteczna. Było to połączenie metod @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);
}
Vincil Bishop
źródło
11

Prawdopodobnie najlepiej będzie użyć metod 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)
}
Panie Stanev
źródło
3
W odniesieniu didEndDisplayingdo dokumentów: informuje delegata, że ​​określona komórka została usunięta z widoku kolekcji. Użyj tej metody, aby wykryć, kiedy komórka została usunięta z widoku kolekcji, w przeciwieństwie do monitorowania samego widoku, aby zobaczyć, kiedy znika. Więc nie sądzę, że didEndDisplayingjest wywoływany, gdy komórka jest wyświetlana.
Jonny
9

Chcę tylko dodać dla innych: z jakiegoś powodu nie otrzymałem komórki, która była widoczna dla użytkownika, gdy przewijałem do poprzedniej komórki w collectionView z pagingEnabled.

Więc wstawiam kod wewnątrz dispatch_async, aby nadać mu trochę powietrza i to działa dla mnie.

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


            [visibleCell doSomthing];
        });
}
user1105951
źródło
To naprawdę pomogło! Nie zdawałem sobie sprawy, że mogę użyć bloku dispatch_async, aby uczynić go absolutnie bezbłędnym! To najlepsza odpowiedź!
kalafun
1
Dla Swift 4: DispatchQueue.main.async {kiedy połączenie inicjuje coś takiego jakfunc tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
Jonny
Pewne wyjaśnienie: to też mi pomogło i miałem ten sam / podobny problem podczas przewijania z powrotem do uitableviewcell zawierającego uicollectionview. Odświeżenie zainicjowane willDisplay celljak wspomniano powyżej. Myślę, że problem gdzieś w UIKit jest tak naprawdę w moim przypadku taki sam jak ta odpowiedź.
Jonny
7

Dla 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
źródło
6

Swift 3.0

Najprostsze rozwiązanie, które da ci indexPath dla widocznych komórek.

yourCollectionView.indexPathsForVisibleItems 

zwróci tablicę indexpath.

Po prostu weź pierwszy obiekt z takiej tablicy.

yourCollectionView.indexPathsForVisibleItems.first

Myślę, że powinno działać dobrze również z Objective - C.

Anil Kukadeja
źródło
6

UICollectionView aktualny widoczny indeks komórek: Swift 3

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

     return nil
}
Mihail Salari
źródło
Najlepsza odpowiedź. Czysto i kilka linii.
garanda
1
Doskonała odpowiedź… Dziękuję
Hardik Thakkar
3

konwersja odpowiedzi @ Anthony'ego na Swift 3.0 działała idealnie dla mnie:

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

źródło
2

Możesz użyć scrollViewDidEndDecelerating: do tego

//@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
źródło
0

To stare pytanie, ale w moim przypadku ...

- (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
źródło
0

Sprawdź również ten fragment

let isCellVisible = collectionView.visibleCells.map { collectionView.indexPath(for: $0) }.contains(inspectingIndexPath)
Nik Kov
źródło
0

W tym wątku jest tak wiele rozwiązań, które działają dobrze, jeśli komórka zajmuje pełny ekran, ale używają granic widoku kolekcji i punktów środkowych widocznego prostego Jednak jest proste rozwiązanie tego problemu

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

w ten sposób możesz uzyskać indexPath widocznej komórki. Dodałem DispatchQueue, ponieważ jeśli przesuniesz szybciej i przez krótką chwilę zostanie wyświetlona następna komórka, to bez dispactchQueue otrzymasz indexPath krótko pokazanej komórki, a nie komórki, która jest wyświetlana na ekranie.

kuldip
źródło
-1

spróbuj tego, to działa. (w poniższym przykładzie mam na przykład 3 komórki).

    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
źródło
-2

Swift 3 i 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])
}

Jeśli chcesz pokazać rzeczywistą liczbę, możesz dodać +1

Jamil Hasnine Tamim
źródło