UICollectionView текущий видимый индекс ячейки

115

Я UICollectionViewвпервые использую приложение для iPad. Я установил UICollectionView, что его размер и размер ячейки одинаковы, что означает, что ячейка отображается только один раз.

Проблема: теперь, когда пользователь прокручивает UICollectionView, мне нужно знать, какая ячейка видна, мне нужно обновлять другие элементы пользовательского интерфейса при изменении. Я не нашел для этого метода делегата. Как я могу этого добиться?

Код:

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

Что я пробовал:

Как UICollectionViewсоответствует UIScrollView, я получил, когда пользовательская прокрутка заканчивается UIScrollViewDelegateметодом

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

Но внутри функции выше, как я могу получить текущий видимый индекс ячейки UICollectionView???

Ирфан ДАТСКИЙ
источник
self.collectionViewFloors.indexPathsForVisibleItems
Aznix,

Ответы:

130

Метод [collectionView visibleCells] дает вам весь массив visibleCells, который вы хотите. Используйте его, когда хотите получить

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

Обновление до Swift 5:

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    for cell in yourCollectionView.visibleCells {
        let indexPath = yourCollectionView.indexPath(for: cell)
        print(indexPath)
    }
}
LE SANG
источник
Не могли бы вы подробнее рассказать, откуда берется self.mainImageCollection? Большое спасибо за вашу дополнительную информацию.
Benjamin McFerren 01
@BenjaminMcFerren - это то, что вы использовали.
LE SANG 02
10
Метод scrollViewDidScroll:делегата обеспечивает обновления представления прокрутки всякий раз, когда заканчивается прокрутка (и, вероятно, здесь лучше). В отличие от scrollViewDidEndDecelerating:метода делегата, который вызывается только тогда, когда представление прокрутки « останавливается [от большой прокрутки] » (см. Заголовок UIScrollView).
Сэм Спенсер,
1
IIRC вызывается ..DidScrollмного раз, даже во время короткой прокрутки. Может быть, а может и нет, в зависимости от того, что человек хочет делать.
ToolmakerSteve
4
Начиная с iOS 10, теперь также можно использовать indexPathsForVisibleItems напрямую :)
Бен
174

indexPathsForVisibleItemsможет работать в большинстве ситуаций, но иногда он возвращает массив с более чем одним индексным путем, и может быть сложно определить тот, который вам нужен. В таких ситуациях вы можете сделать что-то вроде этого:

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

Это особенно хорошо работает, когда каждый элемент в вашем представлении коллекции занимает весь экран.

Свифт версия

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
источник
9
Я согласен, иногда indexPathsForVisibleItems возвращает больше ячеек, даже если мы думаем, что такого быть не должно. Ваше решение отлично работает.
MP23
2
Я был полностью в такой ситуации, всего одно исправление, которое сработало для меня (но мой случай был с табличным представлением), мне нужно было изменить CGPointMake (CGRectGetMidX (visibleRect), CGRectGetMidY (visibleRect)); для CGPointMake (CGRectGetMidX (visibleRect), CGRectGetMinY (visibleRect));
JRafaelM
1
Отлично, но если между ячейками есть промежуток, то visibleIndexPathиногда будет ноль, Итакif (visibleIndexPath) == nil { let cells = collectionView.visibleCells() let visibleIndexPath = collectionView.indexPathForCell(cells[0] as! UICollectionViewCell)! as NSIndexPath }
Хусам
Единственное решение, которое сработало для моих полноэкранных ячеек. Добавлена ​​версия Swift в качестве ответа ниже.
Сэм Бинг
1
Хотел бы я проголосовать за это больше. Эта проблема сводила меня с ума, и это решение спасло положение.
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)
}

Рабочие ответы, объединенные в 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)

    }
Сэм Бинг
источник
Получив indexpath дважды, мне нужно получить только один раз
Аршад Шайк
18

Для полноты картины это метод, который в итоге сработал для меня. Это была комбинация методов @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

Вероятно, лучше всего будет использовать методы 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
Что касается didEndDisplayingдокументации: сообщает делегату, что указанная ячейка была удалена из представления коллекции. Используйте этот метод, чтобы определить, когда ячейка удаляется из представления коллекции, в отличие от мониторинга самого представления, чтобы увидеть, когда оно исчезнет. Так что я не думаю, что didEndDisplayingвызывается при отображении ячейки.
Джонни
9

Просто хочу добавить для других: по какой-то причине я не получил ячейку, которая была видна пользователю, когда я прокручивал предыдущую ячейку в collectionView с помощью pagingEnabled.

Поэтому я вставляю код в dispatch_async, чтобы придать ему немного «воздуха», и у меня это работает.

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


            [visibleCell doSomthing];
        });
}
user1105951
источник
Это действительно помогло! Я не понимал, что могу использовать блок dispatch_async, чтобы сделать его абсолютно безупречным! Это лучший ответ!
kalafun
1
Для Swift 4: DispatchQueue.main.async {когда вызов инициируется чем-то вродеfunc tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
Джонни,
Некоторое пояснение: это тоже помогло мне, и у меня была такая же / аналогичная проблема при прокрутке назад к uitableviewcell, содержащему uicollectionview. Вызов обновления инициирован, willDisplay cellкак указано выше. Я думаю, что проблема где-то в UIKit в моем случае действительно такая же, как и этот ответ.
Джонни
7

Для 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

Самое простое решение, которое даст вам indexPath для видимых ячеек.

yourCollectionView.indexPathsForVisibleItems 

вернет массив indexpath.

Просто возьмите первый объект из такого массива.

yourCollectionView.indexPathsForVisibleItems.first

Думаю, он должен работать и с Objective-C.

Анил Кукадея
источник
6

UICollectionView текущий видимый индекс ячейки: Swift 3

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

     return nil
}
Михаил Салари
источник
Лучший ответ. Чисто и несколько строк.
гарантия 01
1
Идеальный ответ .. Спасибо
Hardik Thakkar
3

преобразование ответа @ Anthony в Swift 3.0 отлично сработало для меня:

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

Вы можете использовать scrollViewDidEndDecelerating: для этого

//@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
источник
0

Это старый вопрос, но в моем случае ...

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

Также проверьте этот фрагмент

let isCellVisible = collectionView.visibleCells.map { collectionView.indexPath(for: $0) }.contains(inspectingIndexPath)
Ник Ков
источник
0

В этом потоке так много решений, которые отлично работают, если ячейка занимает полноэкранный режим, но они используют границы представления коллекции и средние точки видимого прямоугольника. Однако есть простое решение этой проблемы.

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

таким образом вы можете получить indexPath видимой ячейки. Я добавил DispatchQueue, потому что, когда вы проводите пальцем быстрее и если на короткое время отображается следующая ячейка, то без dispactchQueue вы получите indexPath кратко отображаемой ячейки, а не ячейки, которая отображается на экране.

kuldip
источник
-1

попробуйте это, это работает. (например, в примере ниже у меня 3 ячейки.)

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

Если вы хотите показать реальное число, вы можете добавить +1

Джамиль Хаснин Тамим
источник