【问题标题】:UICollectionView: current index path for page controlUICollectionView:页面控件的当前索引路径
【发布时间】:2014-01-04 18:40:02
【问题描述】:

我正在使用带有流布局的 UICollectionView 来显示单元格列表,我还有一个页面控件来指示当前页面,但似乎没有办法获取当前索引路径,我知道我可以看到细胞:

UICollectionView current visible cell index

但是可以有多个可见单元格,即使我的每个单元格都占据屏幕的整个宽度,如果我将其滚动到两个单元格的两半,那么它们都是可见的,那么有没有办法只获取一个当前可见单元格的索引?

谢谢

【问题讨论】:

    标签: ios objective-c pagination uicollectionview uipagecontrol


    【解决方案1】:

    在scrollViewDidScroll委托中监听contentOffset可以获取当前索引

    会是这样的

    -(void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        NSInteger currentIndex = self.collectionView.contentOffset.x / self.collectionView.frame.size.width;
    
    }
    

    【讨论】:

    • 这很好,但我发现如果您需要更新 UI 某些内容,将相同的代码放入 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 会提供更流畅的效果。
    • 我可以建议:NSInteger currentIndex = (NSInteger)(self.collectionView.contentOffset.x / self.collectionView.frame.size.width + 0.5);?当有半页或页面包含部分显示的集合视图单元格时,这会提供更好的结果。
    • 效果很好!尤其是@eunoia 的建议。谢谢!
    【解决方案2】:

    通过 NSIndexPath 从视图中心获取页面。

    即使您的页面不等于 UICollectionView 的宽度也可以工作。

    func scrollViewDidScroll(scrollView: UIScrollView) {
        let xPoint = scrollView.contentOffset.x + scrollView.frame.width / 2
        let yPoint = scrollView.frame.height / 2
        let center = CGPoint(x: xPoint, y: yPoint)
        if let ip = collectionView.indexPathForItemAtPoint(center) {
            self.pageControl.currentPage = ip.row
        }
    }
    

    【讨论】:

    • 不错的方法,我会在做作之前添加一个从 ip.rowpageControl.currentPage 的相等测试,以避免在 UIPageControl 逻辑上触发无用的开销
    【解决方案3】:

    当滚动移动停止时,你肯定需要捕捉可见的项目。使用下一个代码来完成。

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        if let indexPath = myCollectionView.indexPathsForVisibleItems.first {
            myPageControl.currentPage = indexPath.row
        }
    }
    

    【讨论】:

    • 如果您的收藏视图处于页面模式并且您的单元格宽度 = 屏幕宽度(如全屏 pageViewController),则它不起作用。结果 indexPath 并不总是正确的。
    • @Rikco 您是否找到了启用页面模式且单元格宽度 = 屏幕宽度的解决方案?这是我的确切情况
    【解决方案4】:

    斯威夫特 5.1

    零崩溃的简单方法和更安全

    func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        if collectionView == newsCollectionView {
            if newsPager.currentPage == indexPath.row {
                guard let visible = newsCollectionView.visibleCells.first else { return }
                guard let index = newsCollectionView.indexPath(for: visible)?.row else { return }
                newsPager.currentPage = index
            }
    
        }
    }
    

    【讨论】:

      【解决方案5】:
      1. PageControl 放在您的视图中或由代码设置。
      2. 设置UIScrollViewDelegate
      3. Collectionview-> cellForItemAtIndexPath(方法)中添加以下内容 计算页数的代码,

        int pages = floor(ImageCollectionView.contentSize.width/ImageCollectionView.frame.size.width);
        [pageControl setNumberOfPages:pages];
        
      4. 添加ScrollView Delegate方法,

        #pragma mark - UIScrollViewDelegate for UIPageControl
        
        - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
        {
            CGFloat pageWidth = ImageCollectionView.frame.size.width;
            float currentPage = ImageCollectionView.contentOffset.x / pageWidth;
        
            if (0.0f != fmodf(currentPage, 1.0f))
            {
                pageControl.currentPage = currentPage + 1;
            }
            else
            {
                pageControl.currentPage = currentPage;
            }
            NSLog(@"finishPage: %ld", (long)pageControl.currentPage);
        }
        

      【讨论】:

      • 那么如果集合视图中有十个单元格,这会计算十次页数吗?一次就够了吗?
      • 添加此代码的更好位置:int pages = floor(ImageCollectionView.contentSize.width/ImageCollectionView.frame.size.width); [pageControl setNumberOfPages:pages]; ???
      【解决方案6】:

      我有类似的情况,我的流布局设置为 UICollectionViewScrollDirectionHorizo​​ntal 并且我正在使用页面控件来显示当前页面。

      我使用custom flow layout 实现了它。

      /------------------------ 自定义标头的标头文件 (.h) ------------------------------------/

      /**
      * The customViewFlowLayoutDelegate protocol defines methods that let you coordinate with
      *location of cell which is centered.
      */
      
      @protocol CustomViewFlowLayoutDelegate <UICollectionViewDelegateFlowLayout>
      
      /** Informs delegate about location of centered cell in grid.
      *  Delegate should use this location 'indexPath' information to 
      *   adjust it's conten associated with this cell. 
      *   @param indexpath of cell in collection view which is centered.
      */
      
      - (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout cellCenteredAtIndexPath:(NSIndexPath *)indexPath;
      @end
      
      @interface customViewFlowLayout : UICollectionViewFlowLayout
      @property (nonatomic, weak) id<CustomViewFlowLayoutDelegate> delegate;
      @end
      

      /------------------- 自定义标头的实现文件 (.m) ------------------ -/

      @implementation customViewFlowLayout
      - (void)prepareLayout {
       [super prepareLayout];
       }
      
      static const CGFloat ACTIVE_DISTANCE = 10.0f; //Distance of given cell from center of visible rect
       static const CGFloat ITEM_SIZE = 40.0f; // Width/Height of cell.
      
      - (id)init {
          if (self = [super init]) {
          self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
          self.minimumInteritemSpacing = 60.0f;
          self.sectionInset = UIEdgeInsetsZero;
          self.itemSize = CGSizeMake(ITEM_SIZE, ITEM_SIZE);
          self.minimumLineSpacing = 0;
      }
          return self;
          }
      
      - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
          return YES;
      }
      
      - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
         NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
      
      CGRect visibleRect;
      visibleRect.origin = self.collectionView.contentOffset;
      visibleRect.size = self.collectionView.bounds.size;
      
      for (UICollectionViewLayoutAttributes *attribute in attributes) {
          if (CGRectIntersectsRect(attribute.frame, rect)) {
      
              CGFloat distance = CGRectGetMidX(visibleRect) - attribute.center.x;
              // Make sure given cell is center
              if (ABS(distance) < ACTIVE_DISTANCE) {
                  [self.delegate collectionView:self.collectionView layout:self cellCenteredAtIndexPath:attribute.indexPath];
              }
          }
      }
      return attributes;
      }
      

      包含集合视图的类必须符合我之前在自定义布局头文件中描述的协议“CustomViewFlowLayoutDelegate”。喜欢:

      @interface MyCollectionViewController () <UICollectionViewDataSource, UICollectionViewDelegate, CustomViewFlowLayoutDelegate>
      @property (strong, nonatomic) IBOutlet UICollectionView *collectionView;
      @property (strong, nonatomic) IBOutlet UIPageControl *pageControl;
      ....
      ....
      @end
      

      有两种方法可以将自定义布局与集合视图挂钩,在 xib 中或在 viewDidLoad 中的代码中:

      customViewFlowLayout *flowLayout = [[customViewFlowLayout alloc]init];
      flowLayout.delegate = self;
      self.collectionView.collectionViewLayout = flowLayout;
      self.collectionView.pagingEnabled = YES; //Matching your situation probably?
      

      最后,在 MyCollectionViewController 实现文件中,实现 'CustomViewFlowLayoutDelegate' 的委托方法。

      - (void)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout cellCenteredAtIndexPath:(NSIndexPath *)indexPath {
      self.pageControl.currentPage = indexPath.row;
      

      }

      我希望这会有所帮助。 :)

      【讨论】:

        【解决方案7】:

        对于 Swift 4.2

        @IBOutlet weak var mPageControl: UIPageControl!
        @IBOutlet weak var mCollectionSlider: UICollectionView!
        
        private var _currentIndex = 0
        private var T1:Timer!
        private var _indexPath:IndexPath = [0,0]
        
        private func _GenerateNextPage(){
            self._currentIndex = mCollectionSlider.indexPathForItem(at: CGPoint.init(x: CGRect.init(origin: mCollectionSlider.contentOffset, size: mCollectionSlider.bounds.size).midX, y: CGRect.init(origin: mCollectionSlider.contentOffset, size: mCollectionSlider.bounds.size).midY))?.item ?? 0
            self.mPageControl.currentPage = self._currentIndex
        }
        func scrollViewDidScroll(_ scrollView: UIScrollView) {
            _SetTimer(AutoScrollInterval)
            _GenerateNextPage()
        }
        
        @objc private func _AutoScroll(){
            self._indexPath = IndexPath.init(item: self._currentIndex+1, section: 0)
            if !(self._indexPath.item < self.numberOfItems){
                _indexPath = [0,0]
            }
            self.mCollectionSlider.scrollToItem(at: self._indexPath, at: .centeredHorizontally, animated: true)
        }
        private func _SetTimer(_ interval:TimeInterval){
            if T1 == nil{
                T1 = Timer.scheduledTimer(timeInterval: interval , target:self , selector: #selector(_AutoScroll), userInfo: nil, repeats: true)
            }
        }
        

        你可以跳过函数 _SetTimer() ,那是自动滚动

        【讨论】:

          【解决方案8】:

          注意 - 我发现 andykkt 的 answer 很有用,但因为它在 obj-c 中,所以将其转换为 swift 并在另一个 @987654322 中实现了逻辑@delegate 以获得更平滑的效果。

          func updatePageNumber() {
              // If not case to `Int` will give an error.
              let currentPage = Int(ceil(scrollView.contentOffset.x / scrollView.frame.size.width))
              pageControl.currentPage = currentPage
          }
          
          func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
              // This will be call when you scrolls it manually.
              updatePageNumber()
          }
          
          func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
              // This will be call when you scrolls it programmatically.
              updatePageNumber()
          }
          

          【讨论】:

          • 如果启用分页,这将不会给出正确答案
          【解决方案9】:

          使用 UICollectionViewDelegate 方法

          func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
              pageControl.currentPage = indexPath.row
          }
          func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
              if pageControl.currentPage == indexPath.row {
                  pageControl.currentPage = collectionView.indexPath(for: collectionView.visibleCells.first!)!.row
              }
          }
          

          【讨论】:

            【解决方案10】:

            斯威夫特 5.0


             extension youriewControllerName:UIScrollViewDelegate{
                    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
                
                        let pageWidth = self.collectionView.frame.size.width
                        pageControl.currentPage = Int(self.collectionView.contentOffset.x / pageWidth)
                    }
                }
            

            【讨论】:

              【解决方案11】:
              (void)scrollViewDidScroll:(UIScrollView *)scrollView
              {
                  CGFloat pageWidth = _cvImagesList.frame.size.width;
                  float currentPage = _cvImagesList.contentOffset.x / pageWidth;
              
                   _pageControl.currentPage = currentPage + 1;
                  NSLog(@"finishPage: %ld", (long)_pageControl.currentPage);
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2015-09-09
                • 1970-01-01
                • 1970-01-01
                • 2021-07-23
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多