UICollectionView現在表示されているセルインデックス


115

UICollectionViewiPadアプリケーションで初めて使用しています。UICollectionViewサイズとセルサイズが同じになるように設定しました。つまり、一度に1つのセルしか表示されません。

問題: ユーザーがUICollectionViewをスクロールするときに、どのセルが表示されているかを知る必要があるので、変更時に他のUI要素を更新する必要があります。このためのデリゲートメソッドは見つかりませんでした。どうすればこれを達成できますか?

コード:

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

self.mainImageCollectionがどこから来ているのか詳しく説明していただけますか?多くの詳細を事前に感謝します。
ベンジャミンマクフェレ

@BenjaminMcFerrenそれはあなたが使用したコレクションビューです。
LE SANG 2014年

10
scrollViewDidScroll:デリゲートメソッドは、いつでも任意のスクロールが終了スクロールビューの更新を提供します(そしておそらくここではより良い選択です)。scrollViewDidEndDecelerating:スクロールビューが「(大きなスクロールから)停止にグラインドする」ときにのみ呼び出されるデリゲートメソッドとは対照的です(UIScrollViewヘッダーを参照)。
サムスペンサー

1
IIRCは..DidScroll、短いスクロール中でも何度も呼び出されます。何をしたいかによって、良いことかもしれませんし、そうでないかもしれません。
ToolmakerSteve 2016年

4
iOS 10以降、indexPathsForVisibleItemsを直接使用することも可能になりました:)
Ben

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

これは、コレクションビューの各アイテムが画面全体を占める場合に特に効果的です。

Swiftバージョン

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 2014

2
私は完全にこの種の状況にありました、私のために働いた1つの修正(ただし私のケースはtableviewでした)、CGPointMake(CGRectGetMidX(visibleRect)、CGRectGetMidY(visibleRect));を変更する必要がありました。CGPointMake(CGRectGetMidX(visibleRect)、CGRectGetMinY(visibleRect));
JRafaelM 2015年

1
すばらしいですが、セル間にスペースがある場合は、visibleIndexPathときどきゼロになることがあります。そのためif (visibleIndexPath) == nil { let cells = collectionView.visibleCells() let visibleIndexPath = collectionView.indexPathForCell(cells[0] as! UICollectionViewCell)! as NSIndexPath }
Husam

全画面サイズのセルで機能する唯一のソリューション。以下の回答としてSwiftバージョンを追加しました。
Sam Bing

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)

    }

indexpathを2度取得し、1度だけ取得する必要がある
Arshad

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

他の人のために追加したいだけです:何らかの理由で、にスクロールしていたときにユーザーに表示されていたセルを取得できませんでした pagingEnabledを使用してcollectionViewでセルに。

そこで、コードを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

いくつかの明確化:これは私にも役立ち、uicollectionviewを含むuitableviewcellにスクロールして戻るときに、同じ/類似の問題がありました。上記のように開始された更新呼び出し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
}

ベストアンサー。クリーンで数行。
ガランダ16

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

    }

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

このスレッドでは、セルが全画面表示になっても正常に機能する解決策がたくさんありますが、それらはコレクションビューの境界とVisible rectの中間点を使用しますが、この問題には簡単な解決策があります

    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を追加できます

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.