UICollectionView当前可见的单元格索引


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

Answers:


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的来源?预先有许多thanx,供您进一步了解。
本杰明·麦克弗伦

@BenjaminMcFerren这是您使用的collectionview。
LE SANG 2014年

10
scrollViewDidScroll:每当滚动完成时,委托方法都会提供滚动视图更新(在这里可能是一个更好的选择)。与scrollViewDidEndDecelerating:仅在滚动视图“ 从大滚动停止到停止 ”时调用的委托方法相反(请参见UIScrollView标头)。
Sam Spencer

1
IIRC,..DidScroll即使在很短的滚动过程中也被多次调用。可能是好事,也可能不是好事,这取决于一个人想做什么。
ToolmakerSteve

4
由于iOS的10它现在也可以直接使用indexPathsForVisibleItems :)

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

当您的收藏夹视图中的每个项目都占据整个屏幕时,此功能特别有用。

迅捷版

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 2014年

2
我完全处于这种情况,只是一个对我有用的修复程序(但我的情况是与tableview一起使用),我需要更改CGPointMake(CGRectGetMidX(visibleRect),CGRectGetMidY(visibleRect));对于CGPointMake(CGRectGetMidX(visibleRect),CGRectGetMinY(visibleRect));
JRFAELM

1
极好,但是如果单元格之间有间隔,则visibleIndexPath有时将为零,所以if (visibleIndexPath) == nil { let cells = collectionView.visibleCells() let visibleIndexPath = collectionView.indexPathForCell(cells[0] as! UICollectionViewCell)! as NSIndexPath }
Husam 2015年

适用于我的全屏尺寸单元格的唯一解决方案。在下面添加了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方法:(快速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,来自docs:告诉委托人指定的单元格已从集合视图中移除。使用此方法来检测何时从集合视图中删除一个单元格,而不是监视视图本身以查看它何时消失。因此,我认为didEndDisplaying在显示单元格时不会调用它。
Jonny

9

只是想为其他用户添加内容:由于某种原因,当我使用分页启用功能滚动到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中某个地方的问题与该答案确实相同。
乔尼

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 '16

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

在此线程中,有很多解决方案都可以在单元格全屏显示的情况下很好地工作,但是它们使用的集合视图范围和可见矩形的中点。但是,有一个简单的解决方案可以解决此问题

    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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.