UICollection ดูดัชนีเซลล์ที่มองเห็นได้ในปัจจุบัน


115

ฉันใช้UICollectionViewแอปพลิเคชัน iPad เป็นครั้งแรก ฉันได้ตั้งค่าUICollectionViewให้ขนาดและขนาดเซลล์เท่ากันหมายถึงแสดงเพียงครั้งเดียวในแต่ละครั้ง

ปัญหา: ตอนนี้เมื่อผู้ใช้เลื่อน 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 มาจากไหน? ขอบคุณล่วงหน้าสำหรับรายละเอียดเพิ่มเติมของคุณ
Benjamin McFerren

@BenjaminMcFerren เป็นคอลเลกชันที่คุณเคยใช้
LE SANG

10
scrollViewDidScroll:วิธีผู้รับมอบสิทธิ์ให้ปรับปรุงมุมมองเลื่อนเสร็จสิ้นเมื่อใดเลื่อน (และอาจจะเป็นทางเลือกที่ดีกว่าที่นี่) ตรงข้ามกับscrollViewDidEndDecelerating:วิธีการมอบหมายซึ่งจะเรียกเฉพาะเมื่อมุมมองการเลื่อน " บดเพื่อหยุด [จากการเลื่อนขนาดใหญ่] " (ดูส่วนหัว UIScrollView)
Sam Spencer

1
IIRC ..DidScrollถูกเรียกหลายครั้งแม้ในช่วงเลื่อนสั้น ๆ อาจจะเป็นเรื่องดีหรือไม่ก็ได้ขึ้นอยู่กับว่าใครอยากทำอะไร
ToolmakerSteve

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

2
ฉันอยู่ในสถานการณ์แบบนี้โดยสิ้นเชิงมีเพียงการแก้ไขเดียวที่ใช้ได้ผลสำหรับฉัน (แต่กรณีของฉันใช้ tableview) ฉันต้องเปลี่ยน 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 }
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 สองครั้งฉันต้องได้เพียงครั้งเดียว
Arshad Shaik

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) {
จอนนี่

คำชี้แจงบางประการ: สิ่งนี้ช่วยฉันได้เช่นกันและฉันก็มีปัญหาเดียวกัน / คล้ายกันเมื่อเลื่อนกลับไปที่ 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

การแปลงคำตอบของ @ 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.