UICollection: Lihat indeks sel yang terlihat saat ini


115

Saya menggunakan UICollectionViewpertama kali di aplikasi iPad saya. Saya telah mengatur UICollectionViewsedemikian rupa sehingga ukuran dan ukuran selnya sama, artinya hanya sekali sel ditampilkan dalam satu waktu.

Masalah: Sekarang ketika pengguna menggulir UICollectionView, saya perlu tahu sel mana yang terlihat, saya harus memperbarui elemen UI lainnya saat diubah. Saya tidak menemukan metode delegasi untuk ini. Bagaimana saya bisa mencapai ini?

Kode:

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

Apa yang telah saya coba:

Sebagai UICollectionViewsesuai dengan UIScrollView, saya mendapat ketika pengguna scroll berakhir dengan UIScrollViewDelegatemetode

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

Tetapi di dalam fungsi di atas bagaimana saya bisa mendapatkan indeks sel yang terlihat saat ini UICollectionView???


self.collectionViewFloors.indexPathsForVisibleItems
Aznix

Jawaban:


130

Metode [collectionView visibleCells] memberi Anda semua array visibleCells yang Anda inginkan. Gunakan saat Anda ingin mendapatkannya

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

Perbarui ke Swift 5:

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

Bisakah Anda menjelaskan dari mana self.mainImageCollection berasal? Banyak terima kasih sebelumnya untuk detail Anda lebih lanjut.
Benjamin McFerren

@BenjaminMcFerren itu collectionview yang Anda gunakan.
LE SANG

10
The scrollViewDidScroll:metode delegasi memberikan pandangan update gulir setiap kali ada selesai scroll (dan mungkin pilihan yang lebih baik di sini). Berbeda dengan scrollViewDidEndDecelerating:metode delegasi yang hanya dipanggil ketika tampilan gulir " terhenti [dari gulir besar] " (lihat header UIScrollView).
Sam Spencer

1
IIRC, ..DidScrolldipanggil berkali-kali, bahkan selama scroll singkat. Mungkin atau mungkin tidak menjadi hal yang baik, tergantung pada apa yang ingin dilakukan.
ToolmakerSteve

4
Sejak iOS 10 sekarang juga memungkinkan untuk menggunakan indexPathsForVisibleItems secara langsung :)
Ben

174

indexPathsForVisibleItemsmungkin berfungsi untuk sebagian besar situasi, tetapi terkadang ia mengembalikan larik dengan lebih dari satu jalur indeks dan bisa jadi rumit untuk mencari tahu yang Anda inginkan. Dalam situasi tersebut, Anda dapat melakukan sesuatu seperti ini:

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

Ini bekerja sangat baik ketika setiap item dalam tampilan koleksi Anda memenuhi seluruh layar.

Versi cepat

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
Saya setuju, terkadang indexPathsForVisibleItems mengembalikan lebih banyak sel, meskipun menurut kami seharusnya tidak ada kasus seperti itu. Solusi Anda bekerja dengan baik.
MP23

2
Saya benar-benar berada dalam situasi seperti ini, hanya satu perbaikan yang berhasil untuk saya (tetapi kasus saya adalah dengan tableview), saya perlu mengubah CGPointMake (CGRectGetMidX (visibleRect), CGRectGetMidY (visibleRect)); untuk CGPointMake (CGRectGetMidX (visibleRect), CGRectGetMinY (visibleRect));
JRafaelM

1
Luar biasa, tetapi jika sel memiliki ruang di antara mereka maka visibleIndexPathkadang-kadang akan menjadi nol, Jadiif (visibleIndexPath) == nil { let cells = collectionView.visibleCells() let visibleIndexPath = collectionView.indexPathForCell(cells[0] as! UICollectionViewCell)! as NSIndexPath }
Husam

Satu-satunya solusi yang berfungsi untuk sel ukuran layar penuh saya. Menambahkan versi Swift sebagai jawaban di bawah.
Sam Bing

1
Saya berharap saya bisa lebih suka ini. Masalah ini telah membuat saya gila dan solusi ini menyelamatkan saya.
SeanT

77

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

Jawaban Kerja Dikombinasikan Dalam 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)

    }

Mendapatkan jalur indeks dua kali, saya hanya perlu sekali
Arshad Shaik

18

Demi kelengkapan, inilah metode yang akhirnya berhasil untuk saya. Itu adalah kombinasi dari metode @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

Mungkin akan lebih baik menggunakan metode 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
Mengenai didEndDisplaying, dari dokumen: Memberi tahu delegasi bahwa sel yang ditentukan telah dihapus dari tampilan koleksi. Gunakan metode ini untuk mendeteksi saat sel dihapus dari tampilan koleksi, bukan memantau tampilan itu sendiri untuk melihat kapan sel tersebut menghilang. Jadi saya tidak berpikir didEndDisplayingdipanggil saat sel ditampilkan.
Jonny

9

Hanya ingin menambahkan untuk yang lain: untuk beberapa alasan, saya tidak mendapatkan sel yang terlihat oleh pengguna saat saya menggulir ke sel sebelumnya di collectionView dengan pagingEnabled.

Jadi saya memasukkan kode di dalam dispatch_async untuk memberinya "udara" dan ini berfungsi untuk saya.

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


            [visibleCell doSomthing];
        });
}

Ini sangat membantu! Saya tidak menyadari bahwa saya dapat menggunakan blok dispatch_async untuk membuatnya benar-benar sempurna! Inilah jawaban terbaik!
kalafun

1
Untuk Swift 4: DispatchQueue.main.async {ketika panggilan dimulai dari sesuatu sepertifunc tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
Jonny

Beberapa klarifikasi: ini membantu saya juga, dan saya memiliki masalah yang sama / serupa, saat menggulir kembali ke uitableviewcell yang berisi uicollectionview. Panggilan penyegaran dimulai dari willDisplay cellseperti yang disebutkan di atas. Saya pikir masalahnya, di suatu tempat di UIKit, dalam kasus saya benar-benar sama dengan jawaban ini.
Jonny

7

Untuk 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.0

Solusi paling sederhana yang akan memberi Anda indexPath untuk sel yang terlihat ..

yourCollectionView.indexPathsForVisibleItems 

akan mengembalikan array dari jalur indeks.

Ambil saja objek pertama dari array seperti ini.

yourCollectionView.indexPathsForVisibleItems.first

Kurasa itu akan bekerja dengan baik dengan Objective - C juga.


6

UICollectionView indeks sel yang terlihat saat ini: Swift 3

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

     return nil
}

Jawaban Terbaik. Bersihkan dan sedikit garis.
garanda

1
Jawaban sempurna .. Terima kasih
Hardik Thakkar

3

mengonversi jawaban @ Anthony ke Swift 3.0 bekerja dengan sempurna untuk saya:

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

Anda dapat menggunakan scrollViewDidEndDecelerating: untuk ini

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

Ini pertanyaan lama tapi dalam kasus saya ...

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

Periksa juga cuplikan ini

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

0

Di utas ini, Ada begitu banyak solusi yang berfungsi dengan baik jika sel mengambil layar penuh tetapi mereka menggunakan batas tampilan kumpulan dan titik tengah dari terlihat persegi Namun ada solusi sederhana untuk masalah ini

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

dengan ini, Anda bisa mendapatkan indexPath dari sel yang terlihat. Saya telah menambahkan DispatchQueue karena ketika Anda menggesek lebih cepat dan jika sesaat sel berikutnya ditampilkan maka tanpa dispactchQueue Anda akan mendapatkan indexPath sel yang ditampilkan secara singkat bukan sel yang ditampilkan di layar.


-1

coba ini, berhasil. (dalam contoh di bawah ini saya memiliki 3 sel misalnya.)

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

Jika Anda ingin menunjukkan nomor sebenarnya daripada Anda dapat menambahkan +1

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.