37

在研究 iOS6 新功能时,我遇到了一个关于UICollectionView的问题。
我目前正在使用 Flow 布局测试它,并将滚动方向设置为水平,启用滚动和分页。我已将其大小设置为与我的自定义单元格完全相同,因此它可以一次显示一个,并且通过横向滚动,用户将看到其他现有单元格。

它完美地工作。

现在我想将UIPageControl添加到我创建的集合视图中,这样它就可以显示哪个单元格是可见的以及那里有多少个单元格。

构建页面控件相当简单,定义了 frame 和 numberOfPages。

作为问号标记,我遇到的问题是如何获取当前在集合视图中可见的单元格,以便它可以更改页面控件的 currentPage。

我尝试过委托方法,例如 cellForItemAtIndexPath,但它是用来加载单元格的,而不是显示它们。didEndDisplayingCell 当一个单元格不再显示时触发,这是我需要的相反事件。

似乎 -visibleCells 和 -indexPathsForVisibleItems 集合视图方法对我来说是正确的选择,但我遇到了另一个问题。什么时候触发它们?

在此先感谢,希望我说得足够清楚,以便你们能理解我!

4

8 回答 8

92

您必须将自己设置为UIScrollViewDelegate并实现如下scrollViewDidEndDecelerating:方法:

Objective-C

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    CGFloat pageWidth = self.collectionView.frame.size.width;
    self.pageControl.currentPage = self.collectionView.contentOffset.x / pageWidth;
}

迅速

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {

    let pageWidth = self.collectionView.frame.size.width
    pageControl.currentPage = Int(self.collectionView.contentOffset.x / pageWidth)
}
于 2013-04-24T14:43:00.237 回答
18

我也为此苦苦挣扎了一段时间,然后我被建议检查 UICollectionView 的父类。其中之一恰好是UIScrollView,如果您将自己设置为UIScrollViewDelegate,您可以访问非常有用的方法,例如scrollViewDidEndDecelerating,这是更新 UIPageControl 的好地方。

于 2012-11-10T04:14:51.743 回答
17

我建议对计算和处理进行一些调整,因为它会在任何滚动位置立即更新页面控件,并具有更高的准确性。

下面的解决方案适用于任何滚动视图或其子类(UITableView UICollectionView 和其他)

在 viewDidLoad 方法中写这个

scrollView.delegate = self

然后使用您的语言的代码:

斯威夫特 3

func scrollViewDidScroll(_ scrollView: UIScrollView) 
    {
       let pageWidth = scrollView.frame.width
       pageControl.currentPage = Int((scrollView.contentOffset.x + pageWidth / 2) / pageWidth)
    }

斯威夫特 2:

func scrollViewDidScroll(scrollView: UIScrollView) 
{
   let pageWidth = CGRectGetWidth(scrollView.frame)
   pageControl.currentPage = Int((scrollView.contentOffset.x + pageWidth / 2) / pageWidth)
}

目标 C

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    CGFloat pageWidth = self.collectionView.frame.size.width;
    self.pageControl.currentPage = (self.collectionView.contentOffset.x + pageWidth / 2) / pageWidth;
}
于 2014-05-08T13:08:30.300 回答
8

另一个使用更少代码的选项是使用可见项索引路径并设置页面控件。

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    self.pageControl.currentPage = [[[self.collectionView indexPathsForVisibleItems] firstObject] row];
}
于 2015-05-20T21:00:44.217 回答
5
  1. PageControl放置在您的视图中或由代码设置。
  2. 设置UIScrollViewDelegate
  3. Collectionview -> cellForItemAtIndexPath (Method) 添加下面的代码来计算页数,

int pages = floor(ImageCollectionView.contentSize.width/ImageCollectionView.frame.size.width); [pageControl setNumberOfPages:pages];

添加ScrollView 委托方法,

杂注标记 - UIPageControl 的 UIScrollVewDelegate

- (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);
}
于 2015-01-05T06:38:01.073 回答
4

我知道这是一个旧的,但我只需要再次实现这种功能并且有一点要添加,它会给出更完整的答案。

首先:使用scrollViewDidEndDecelerating假设用户在拖动时抬起手指(更像是轻弹动作),因此存在减速阶段。如果用户在没有抬起手指的情况下拖动,UIPageControl则仍会指示拖动开始之前的旧页面。相反,使用scrollViewDidScroll回调意味着视图在拖动和轻弹之后以及在拖动和滚动期间都会更新,因此对用户来说感觉更加灵敏和准确。

其次:依靠pagewidth计算所选索引假设所有单元格具有相同的宽度并且每个屏幕有一个单元格。利用该indexPathForItemAtPoint方法UICollectionView可提供更具弹性的结果,适用于不同的布局和单元格大小。下面的实现假设框架的中心是要在pagecontrol. 此外,如果存在单元格间距,则在滚动期间有时 selectedIndex 可能为 nil 或可选,因此需要在 pageControl 上设置之前检查并展开。

 func scrollViewDidScroll(scrollView: UIScrollView) {
   let contentOffset = scrollView.contentOffset
   let centrePoint =  CGPointMake(
    contentOffset.x + CGRectGetMidX(scrollView.frame),
    contentOffset.y + CGRectGetMidY(scrollView.frame)
   )
   if let index = self.collectionView.indexPathForItemAtPoint(centrePoint){
     self.pageControl.currentPage = index.row
   }
 }

还有一件事 - 设置页数,UIPageControl如下所示:

  func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    self.pageControl.numberOfPages = 20
    return self.pageControl.numberOfPages
  } 
于 2016-02-06T14:54:09.917 回答
3

简单的斯威夫特

public func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
    pageControl.currentPage = (collectionView.indexPathsForVisibleItems().first?.row)!
}

UIScrollViewDelegate如果您实施,则已经实施UICollectionViewDelegate

于 2015-10-06T14:30:03.780 回答
0

如果使用scrollViewDidScroll,则应手动更新页面控件以⚠️避免点击页面控件时闪烁的点。

设置UIPageControl.

let pageControl = UIPageControl()
pageControl.pageIndicatorTintColor = .label
pageControl.defersCurrentPageDisplay = true // Opt-out from automatic display
pageControl.numberOfPages = viewModel.items.count
pageControl.addTarget(self, action: #selector(pageControlValueChanged), for: .valueChanged)

实施操作(使用下面的扩展)。

@objc func pageControlValueChanged(_ sender: UIPageControl) {
    collectionView.scroll(to: sender.currentPage)
}

UIPageControl在每次滚动时手动更新。

extension ViewController: UIScrollViewDelegate {
        
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        pageControl.currentPage = collectionView.currentPage
        pageControl.updateCurrentPageDisplay() // Display only here
    }
}

方便UICollectionView的扩展。

extension CGRect {
    
    var middle: CGPoint {
        CGPoint(x: midX, y: midY)
    }
}

extension UICollectionView {
    
    var visibleArea: CGRect {
        CGRect(origin: contentOffset, size: bounds.size)
    }
    
    var currentPage: Int {
        indexPathForItem(at: visibleArea.middle)?.row ?? 0
    }
    
    func scroll(to page: Int) {
        scrollToItem(
            at: IndexPath(row: page, section: 0),
            at: .centeredHorizontally,
            animated: true
        )
    }
}
于 2021-01-07T00:06:48.560 回答