UICollectionView 현재 보이는 셀 인덱스


115

UICollectionViewiPad 응용 프로그램에서 처음 사용하고 있습니다. I가 설정 한 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 2014-06-01

@BenjaminMcFerren 당신이 사용한 collectionview입니다.
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)); for CGPointMake (CGRectGetMidX (visibleRect), CGRectGetMinY (visibleRect));
JRafaelM

1
세포 사이에 공간이있는 경우 우수,하지만 visibleIndexPath때로는 그래서 nil이 될 것입니다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 받기, 내가 한 번만받을 필요
아르 샤드 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셀이 표시 될 때 호출 되지 않는다고 생각 합니다.
Jonny

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의 어딘가에서 문제가 내 경우이 대답과 실제로 동일하다고 생각합니다.
Jonny

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

스위프트 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
}

최고의 답변입니다. 깨끗하고 몇 줄.
garanda

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를 얻을 수 있습니다. 더 빨리 스 와이프하고 잠시 동안 다음 셀이 dispactchQueue없이 표시되면 화면에 표시되는 셀이 아니라 잠깐 표시된 셀의 indexPath가 표시되기 때문에 DispatchQueue를 추가했습니다.


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