136

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

4

16 回答 16

209

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

当您的集合视图中的每个项目占据整个屏幕时,这尤其有效。

斯威夫特版本

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)
于 2014-06-24T21:28:34.473 回答
151

方法 [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)
    }
}
于 2013-09-06T04:59:57.773 回答
92

斯威夫特 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)

    }
于 2016-04-11T12:44:49.507 回答
19

为了完整起见,这是最终为我工作的方法。这是@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);
}
于 2014-09-18T13:06:44.087 回答
14

UICollectionView 当前可见单元格索引:Swift 3、4 和 5+

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

作为扩展:

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

用法:

if let indexPath = collectionView.visibleCurrentCellIndexPath { 
   /// do something
}
于 2016-11-24T12:26:05.360 回答
12

最好使用 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)
}
于 2017-06-20T14:08:21.667 回答
10

只想为其他人添加:由于某种原因,当我使用 pagingEnabled 滚动到 collectionView 中的上一个单元格时,我没有得到用户可见的单元格。

所以我在 dispatch_async 中插入代码给它一些“空气”,这对我有用。

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


            [visibleCell doSomthing];
        });
}
于 2015-03-21T21:54:44.187 回答
10

斯威夫特 3.0

最简单的解决方案,它将为您提供可见单元格的 indexPath ..

yourCollectionView.indexPathsForVisibleItems 

将返回 indexpath 的数组。

只需像这样从数组中取出第一个对象。

yourCollectionView.indexPathsForVisibleItems.first

我想它也应该适用于 Objective-C。

于 2017-05-29T05:05:53.860 回答
8

对于 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)
}
于 2017-01-28T12:23:40.127 回答
4

将@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)!")
}
于 2017-05-10T07:08:24.960 回答
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);
        }

    }
于 2014-10-15T13:28:25.497 回答
1

在这个线程中,如果单元格采用全屏模式,有很多解决方案可以正常工作,但它们使用集合视图边界和可见矩形的中点但是有一个简单的解决方案可以解决这个问题

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

这样,您可以获得可见单元格的 indexPath。我添加了 DispatchQueue,因为当您快速滑动时,如果在短时间内显示下一个单元格,那么在没有 dispactchQueue 的情况下,您将获得短暂显示单元格的 indexPath,而不是屏幕上显示的单元格。

于 2020-01-29T06:47:09.547 回答
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;
}
于 2016-05-23T14:00:13.443 回答
0

还要检查这个片段

let isCellVisible = collectionView.visibleCells.map { collectionView.indexPath(for: $0) }.contains(inspectingIndexPath)
于 2018-05-22T07:37:53.437 回答
-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
        }
    }
于 2016-11-22T05:21:44.220 回答
-1

斯威夫特 3 和斯威夫特 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

于 2018-11-11T07:01:36.150 回答