UICollectionПереглянути поточний індекс видимих ​​комірок


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] надає вам весь масив видимихCells, який ви хочете. Використовуйте його, коли хочете отримати

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

Чи можете ви, будь ласка, детальніше пояснити, звідки походить self.mainImageCollection? Багато заздалегідь подякуйте за вашу детальну інформацію
Бенджамін МакФеррен

@BenjaminMcFerren - це використаний вигляд колекції.
LE SANG

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)

9
Я можу погодитися, іноді indexPathsForVisibleItems повертає більше комірок, навіть якщо ми вважаємо, що такого випадку не повинно бути. Ваше рішення чудово працює.
MP23

2
Я повністю опинився в подібній ситуації, лише одне виправлення, яке працювало на мене (але мій випадок був із переглядом таблиці), мені потрібно було змінити CGPointMake (CGRectGetMidX (видимо), CGRectGetMidY (видима); для 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

Швидкий 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)

    }

Обираючи шлях в два рази, мені потрібно пройти лише один раз
Аршад Шейк

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

Це справді допомогло! Я не зрозумів, що можу використовувати блок dispatch_async, щоб зробити його абсолютно бездоганним! Це найкраща відповідь!
kalafun

1
Для Swift 4: DispatchQueue.main.async {коли виклик починається з чогось подібногоfunc tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
Jonny

Деякі роз’яснення: і це мені допомогло, і у мене була та сама / схожа проблема, коли прокручувався назад до 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

UICollectionПерегляд поточного індексу видимих ​​комірок: Swift 3

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

     return nil
}

Найкраща відповідь. Чистота і кілька рядків.
гаранда

1
Ідеальна відповідь .. Дякую
Hardik Thakkar

3

перетворення @ відповіді Ентоні на 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);
        }

    }

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 коротко показаної комірки, а не комірки, яка відображається на екрані.


-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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.