【问题标题】:UICollectionView current visible cell indexUICollectionView 当前可见单元格索引
【发布时间】:2013-09-10 02:10:00
【问题描述】:

我第一次在我的 iPad 应用程序中使用UICollectionView。 我设置了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

标签: ios objective-c ipad uicollectionview


【解决方案1】:

方法 [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 的来源?非常感谢您提供更多详细信息。
  • @BenjaminMcFerren 这是你使用的collectionview。
  • scrollViewDidScroll: 委托方法在任何滚动完成时提供滚动视图更新(这里可能是更好的选择)。与 scrollViewDidEndDecelerating: 委托方法相反,该方法仅在滚动视图“[从大滚动] 停止时调用(参见 UIScrollView 标题)。
  • IIRC, ..DidScroll 被多次调用,即使在短滚动期间也是如此。可能是也可能不是一件好事,这取决于一个人想做什么。
  • 从 iOS 10 开始,现在还可以直接使用 indexPathsForVisibleItems :)
【解决方案2】:

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)

【讨论】:

  • 我同意,有时 indexPathsForVisibleItems 会返回更多单元格,即使我们认为不应该出现这种情况。您的解决方案效果很好。
  • 我完全处于这种情况,只有一个对我有用的修复程序(但我的情况是使用 tableview),我需要更改 CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMidY(visibleRect)) ; for CGPointMake(CGRectGetMidX(visibleRect), CGRectGetMinY(visibleRect));
  • 很好,但是如果单元格之间有空格,那么visibleIndexPath 有时会为零,所以if (visibleIndexPath) == nil { let cells = collectionView.visibleCells() let visibleIndexPath = collectionView.indexPathForCell(cells[0] as! UICollectionViewCell)! as NSIndexPath }
  • 唯一适用于我的全屏尺寸单元格的解决方案。在下面添加了 Swift 版本作为答案。
  • 我希望我能更多地支持这个。这个问题一直让我发疯,这个解决方案挽救了一天。
【解决方案3】:

为了完整起见,这是最终为我工作的方法。这是@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);
}

【讨论】:

    【解决方案4】:

    您可以为此使用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);
            }
    
        }
    

    【讨论】:

      【解决方案5】:

      只是想为其他人添加: 出于某种原因,当我使用 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 块让它绝对完美!这是最好的答案!
      • 对于 Swift 4:DispatchQueue.main.async { 当从 func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { 之类的地方发起呼叫时
      • 一些澄清:这对我也有帮助,当滚动回包含 uicollectionview 的 uitableviewcell 时,我遇到了相同/类似的问题。如上所述,从willDisplay cell 发起的刷新调用。我认为这个问题在 UIKit 的某个地方,在我的情况下和这个答案是一样的。
      【解决方案6】:

      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)
      }
      

      在 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两次,我只需要获取一次
      【解决方案7】:

      这是一个老问题,但就我而言......

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

      【讨论】:

        【解决方案8】:

        试试这个,它有效。 (例如,在下面的示例中,我有 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
                }
            }
        

        【讨论】:

          【解决方案9】:

          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
          }
          

          【讨论】:

          • 最佳答案。干净和几行。
          • 完美答案.. 谢谢
          【解决方案10】:

          对于 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)
          }
          

          【讨论】:

            【解决方案11】:

            将@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)!")
            }
            

            【讨论】:

              【解决方案12】:

              斯威夫特 3.0

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

              yourCollectionView.indexPathsForVisibleItems 
              

              将返回索引路径的数组。

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

              yourCollectionView.indexPathsForVisibleItems.first
              

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

              【讨论】:

                【解决方案13】:

                最好使用 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)
                }
                

                【讨论】:

                • 关于didEndDisplaying,来自文档:告诉代理指定的单元格已从集合视图中删除。使用此方法来检测何时从集合视图中删除单元格,而不是监视视图本身以查看它何时消失。所以我认为在显示单元格时不会调用didEndDisplaying。跨度>
                【解决方案14】:

                还要检查这个 sn-p

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

                【讨论】:

                  【解决方案15】:

                  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

                  【讨论】:

                    【解决方案16】:

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

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

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

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2015-10-13
                      • 2023-03-31
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2019-03-15
                      相关资源
                      最近更新 更多