UICollectionXem chỉ số ô hiển thị hiện tại


115

Tôi đang sử dụng UICollectionViewlần đầu tiên trong ứng dụng iPad của mình. Tôi đã đặt UICollectionViewsao cho kích thước và kích thước ô của nó giống nhau, có nghĩa là chỉ một lần ô được hiển thị tại một thời điểm.

Vấn đề: Bây giờ khi người dùng cuộn UICollectionView, tôi cần biết ô nào hiển thị, tôi phải cập nhật các phần tử giao diện người dùng khác khi thay đổi. Tôi không tìm thấy bất kỳ phương pháp ủy quyền nào cho việc này. Làm thế nào tôi có thể đạt được điều này?

Mã:

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

Những gì tôi đã thử:

Khi UICollectionViewPhù hợp với UIScrollView, tôi nhận được khi cuộn người dùng kết thúc bằng UIScrollViewDelegatephương thức

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

Nhưng bên trong hàm trên làm thế nào tôi có thể nhận được chỉ số tế bào hiển thị hiện tại của UICollectionView???


self.collectionViewFloors.indexPathsForVisibleItems
Aznix

Câu trả lời:


130

Phương thức [collectionView hiddenCells] cung cấp cho bạn tất cả mảng hiddenCells mà bạn muốn. Sử dụng nó khi bạn muốn lấy

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

Cập nhật lên Swift 5:

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

Bạn có thể vui lòng giải thích rõ self.mainImageCollection đến từ đâu không? Nhiều hơnx trước để biết thêm chi tiết của bạn.
Benjamin McFerren

@BenjaminMcFerren đó là chế độ xem bộ sưu tập mà bạn đã sử dụng.
LÊ SANG

10
Các scrollViewDidScroll:phương pháp đại biểu cung cấp thông tin cập nhật xem di chuyển bất cứ khi nào kết thúc cuộn (và có lẽ là một lựa chọn tốt hơn ở đây). Trái ngược với scrollViewDidEndDecelerating:phương thức ủy nhiệm chỉ được gọi khi chế độ xem cuộn " tạm dừng [từ cuộn lớn] " (xem tiêu đề UIScrollView).
Sam Spencer

1
IIRC, ..DidScrollđược gọi nhiều lần, ngay cả trong một cuộn ngắn. Có thể là một điều tốt hoặc không, tùy thuộc vào những gì người ta muốn làm.
ToolmakerSteve

4
Kể từ iOS 10, giờ đây cũng có thể sử dụng trực tiếp indexPathsForVosystemItems :)
Ben

174

indexPathsForVisibleItemscó thể hoạt động trong hầu hết các trường hợp, nhưng đôi khi nó trả về một mảng có nhiều hơn một đường dẫn chỉ mục và có thể khó tìm ra đường dẫn bạn muốn. Trong những tình huống đó, bạn có thể làm như sau:

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

Điều này đặc biệt hiệu quả khi mỗi mục trong chế độ xem bộ sưu tập của bạn chiếm toàn bộ màn hình.

Phiên bản 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
Tôi có thể đồng ý, đôi khi indexPathsForVbrokenItems trả về nhiều ô hơn, ngay cả khi chúng tôi nghĩ rằng không nên có trường hợp như vậy. Giải pháp của bạn hoạt động tuyệt vời.
MP23

2
Tôi hoàn toàn gặp phải tình huống này, chỉ có một bản sửa lỗi phù hợp với tôi (nhưng trường hợp của tôi là với tableview), tôi cần thay đổi CGPointMake (CGRectGetMidX (displayRect), CGRectGetMidY (displayRect)); cho CGPointMake (CGRectGetMidX (hiển thị), CGRectGetMinY (hiển thị));
JRafaelM

1
Tuyệt vời, nhưng nếu các tế bào có một không gian giữa chúng sau đó visibleIndexPathđôi khi sẽ là con số không, Vì vậy,if (visibleIndexPath) == nil { let cells = collectionView.visibleCells() let visibleIndexPath = collectionView.indexPathForCell(cells[0] as! UICollectionViewCell)! as NSIndexPath }
Husam

Giải pháp duy nhất hoạt động cho các ô kích thước toàn màn hình của tôi. Đã thêm phiên bản Swift như một câu trả lời bên dưới.
Sam Bing

1
Tôi ước tôi có thể ủng hộ điều này nhiều hơn. Vấn đề này đã khiến tôi phát điên lên và giải pháp này đã cứu được một ngày.
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)
}

Các câu trả lời làm việc được kết hợp trong 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)

    }

Bắt indexPath hai lần, tôi cần phải có được một lần duy nhất
Arshad Shaik

18

Vì lợi ích hoàn chỉnh, đây là phương pháp cuối cùng đã hiệu quả với tôi. Đó là sự kết hợp giữa các phương pháp của @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

Có lẽ tốt nhất bạn nên sử dụng các phương thức 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
Về didEndDisplaying, từ tài liệu: Cho đại biểu biết rằng ô được chỉ định đã bị xóa khỏi dạng xem bộ sưu tập. Sử dụng phương pháp này để phát hiện khi một ô bị xóa khỏi chế độ xem bộ sưu tập, trái ngược với việc theo dõi chính chế độ xem để xem khi nào nó biến mất. Vì vậy, tôi không nghĩ didEndDisplayinglà được gọi khi ô được hiển thị.
Jonny

9

Chỉ muốn thêm cho những người khác: vì một số lý do, tôi không nhận được ô hiển thị cho người dùng khi tôi cuộn đến ô trước đó trong collectionView với pagingEnabled.

Vì vậy, tôi chèn mã vào bên trong accept_async để cung cấp cho nó một chút "không khí" và điều này phù hợp với tôi.

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


            [visibleCell doSomthing];
        });
}

Điều này thực sự hữu ích! Tôi đã không nhận ra rằng tôi có thể sử dụng khối send_async để làm cho nó trở nên hoàn hảo! Đây là câu trả lời tốt nhất!
kalafun

1
Đối với Swift 4: DispatchQueue.main.async {khi tu cú điện thoại từ một cái gì đó giống nhưfunc tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
Jonny

Một số giải thích rõ: điều này cũng giúp tôi và tôi đã gặp vấn đề tương tự / tương tự, khi cuộn trở lại uitableviewcell chứa uicollectionview. Cuộc gọi làm mới bắt đầu từ willDisplay cellnhư đã đề cập ở trên. Tôi nghĩ rằng vấn đề, ở đâu đó trong UIKit, thực sự giống trong trường hợp của tôi với câu trả lời này.
Jonny

7

Đối với 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

Giải pháp đơn giản nhất sẽ cung cấp cho bạn indexPath cho các ô hiển thị ..

yourCollectionView.indexPathsForVisibleItems 

sẽ trả về mảng đường dẫn chỉ mục.

Chỉ cần lấy đối tượng đầu tiên từ mảng như thế này.

yourCollectionView.indexPathsForVisibleItems.first

Tôi đoán nó cũng sẽ hoạt động tốt với Objective - C.


6

UICollectionXem chỉ số ô hiển thị hiện tại: Swift 3

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

     return nil
}

Câu trả lời tốt nhất. Làm sạch và một vài dòng.
garanda

1
Câu trả lời hoàn hảo .. Cảm ơn bạn
Hardik Thakkar

3

chuyển đổi câu trả lời của @ Anthony thành Swift 3.0 hoạt động hoàn hảo đối với tôi:

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

Bạn có thể sử dụng scrollViewDidEndDecelerating: cho việc này

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

Đây là câu hỏi cũ nhưng trong trường hợp của tôi ...

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

Cũng kiểm tra đoạn mã này

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

0

Trong chủ đề này, Có rất nhiều giải pháp hoạt động tốt nếu ô chiếm toàn màn hình nhưng chúng sử dụng giới hạn chế độ xem bộ sưu tập và điểm giữa của Trực tiếp hiển thị Tuy nhiên, có một giải pháp đơn giản cho vấn đề này

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

bằng cách này, bạn có thể nhận được indexPath của ô hiển thị. Tôi đã thêm DispatchQueue bởi vì khi bạn vuốt nhanh hơn và nếu trong giây lát ô tiếp theo được hiển thị thì không có dispactchQueue, bạn sẽ nhận được indexPath của ô được hiển thị ngắn gọn chứ không phải ô đang được hiển thị trên màn hình.


-1

hãy thử điều này, nó hoạt động. (trong ví dụ dưới đây, tôi có 3 ô chẳng hạn.)

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

Nếu bạn muốn hiển thị số lượng thực tế, bạn có thể thêm +1

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.