简体   繁体   English

iOS6 UICollectionView 和 UIPageControl - 如何获取可见单元格?

[英]iOS6 UICollectionView and UIPageControl - How to get visible cell?

While studying iOS6 new features I got a question about UICollectionView .在学习 iOS6 新功能时,我有一个关于UICollectionView的问题。
I am currently testing it with Flow layout and the scroll direction set to horizontal, scrolling and paging enabled.我目前正在使用 Flow 布局和滚动方向设置为水平、滚动和分页来测试它。 I've set its size to exactly the same as my custom's cells, so it can show one at a time, and by scrollig it sideways, the user would see the other existing cells.我已将其大小设置为与我自定义的单元格完全相同,因此它可以一次显示一个,并且通过将其向侧面滚动,用户将看到其他现有单元格。

It works perfectly.它完美地工作。

Now I want to add and UIPageControl to the collection view I made, so it can show up which cell is visible and how many cells are there.现在我想将UIPageControl添加到我创建的集合视图中,以便它可以显示哪个单元格可见以及有多少个单元格。

Building up the page control was rather simple, frame and numberOfPages defined.构建页面控件相当简单,frame 和 numberOfPages 已定义。

The problem I am having, as the question titles marks, is how to get which cell is currently visible in the collection view, so it can change the currentPage of the page control.正如问题标题所标记的,我遇到的问题是如何获取当前在集合视图中可见的单元格,以便它可以更改页面控件的 currentPage。

I've tried delegate methods, like cellForItemAtIndexPath, but it is made to load cells, not show them.我试过委托方法,比如 cellForItemAtIndexPath,但它是用来加载单元格的,而不是显示它们。 didEndDisplayingCell triggers when a cell its not displayed anymore, the opposite event of what I need. didEndDisplayingCell 在单元格不再显示时触发,这是我需要的相反事件。

Its seems that -visibleCells and -indexPathsForVisibleItems, collection view methods, are the correct choice for me, but I bumped into another problem.看起来集合视图方法 -visibleCells 和 -indexPathsForVisibleItems 是我的正确选择,但我遇到了另一个问题。 When to trigger them?什么时候触发它们?

Thanks in advance, hope I made myself clear enough so you guys can understand me!在此先感谢,希望我说得足够清楚,以便你们能理解我!

You must setup yourself as UIScrollViewDelegate and implement the scrollViewDidEndDecelerating: method like so:您必须将自己设置为UIScrollViewDelegate并实现scrollViewDidEndDecelerating:方法,如下所示:

Objective-C目标-C

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

Swift迅速

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {

    let pageWidth = self.collectionView.frame.size.width
    pageControl.currentPage = Int(self.collectionView.contentOffset.x / pageWidth)
}

I struggled with this for a while as well, then I was advised to check out the parent classes of UICollectionView.我也为此挣扎了一段时间,然后我被建议检查 UICollectionView 的父类。 One of them happens to be UIScrollView and if you set yourself up as a UIScrollViewDelegate , you get access to very helpful methods such as scrollViewDidEndDecelerating , a great place to update the UIPageControl.其中之一恰好是UIScrollView ,如果您将自己设置为UIScrollViewDelegate ,您就可以访问非常有用的方法,例如scrollViewDidEndDecelerating ,这是更新 UIPageControl 的好地方。

I would recommend a little tuned calculation and handling as it will update page control immediately in any scroll position with better accuracy.我建议稍微调整计算和处理,因为它会在任何滚动位置立即更新页面控制,并具有更高的准确性。

The solution below works with any scroll view or it subclass (UITableView UICollectionView and others)下面的解决方案适用于任何滚动视图或其子类(UITableView UICollectionView 等)

in viewDidLoad method write this在 viewDidLoad 方法中写这个

scrollView.delegate = self

then use code for your language:然后使用您的语言代码:

Swift 3斯威夫特 3

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

Swift 2:斯威夫特 2:

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

Objective C目标 C

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

Another option with less code is to use visible item index path and set the page control.另一个代码较少的选项是使用可见项索引路径并设置页面控件。

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    self.pageControl.currentPage = [[[self.collectionView indexPathsForVisibleItems] firstObject] row];
}
  1. Place PageControl in your view or set by Code.PageControl放在您的视图中或由代码设置。
  2. Set UIScrollViewDelegate设置UIScrollViewDelegate
  3. In Collectionview -> cellForItemAtIndexPath (Method) add the below code for calculate the Number of pages,Collectionview -> cellForItemAtIndexPath (Method) 中添加以下代码来计算页数,

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

Add the ScrollView Delegate method,添加ScrollView 委托方法,

pragma mark - UIScrollVewDelegate for UIPageControl pragma mark - UIScrollVewDelegate 用于 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);
}

I know this is an old one but I've just needed to implement this sort of feature again and have a bit to add which gives a more complete answer.我知道这是一个旧的,但我只需要再次实现这种功能并添加一些内容以提供更完整的答案。

Firstly: Using scrollViewDidEndDecelerating assumes that the user lifted their finger while dragging (more like a flick action) and therefore there is a deceleration phase.首先:使用scrollViewDidEndDecelerating假设用户在拖动时抬起手指(更像是轻弹动作),因此存在减速阶段。 If the user drags without lifting the finger the UIPageControl will still indicate the old page from before the drag began.如果用户在不抬起手指的情况下进行拖动, UIPageControl仍将指示拖动开始之前的旧页面。 Instead using the scrollViewDidScroll callback means that the view is updated both after dragging and flicking and also during dragging and scrolling so it feels much more responsive and accurate for the user.相反,使用scrollViewDidScroll回调意味着视图在拖动和轻弹之后以及在拖动和滚动期间都会更新,因此对用户来说感觉更加灵敏和准确。

Secondly: Relying on the pagewidth for calculating the selected index assumes all the cells have the same width and that there is one cell per screen.其次:依托pagewidth ,用于计算所选索引假定所有单元具有相同的宽度,并且有每屏一个小区。 taking advantage of the indexPathForItemAtPoint method on UICollectionView gives a more resilient result which will work for different layouts and cell sizes.取的优点indexPathForItemAtPoint上方法UICollectionView给出了一个更有弹性的结果,这将对于不同的布局和小区大小工作。 The implementation below assumes the centre of the frame is the desired cell to be represented in the pagecontrol .下面的实现假设框架的中心是要在pagecontrol表示的所需单元格。 Also if there are intercell spacings there will times during scrolling when the selectedIndex could be nil or optional so this needs to be checked and unwrapped before setting on the pageControl.此外,如果在滚动期间有单元格间距,则 selectedIndex 可能为零或可选,因此需要在设置 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
   }
 }

One more thing - set the number of pages on the UIPageControl with something like this:还有一件事 - 使用以下内容设置UIPageControl上的UIPageControl

  func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    self.pageControl.numberOfPages = 20
    return self.pageControl.numberOfPages
  } 

Simple Swift简单的斯威夫特

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

UIScrollViewDelegate is already implemented if you implement UICollectionViewDelegate如果你实现了UICollectionViewDelegate就已经实现了UIScrollViewDelegate

If using scrollViewDidScroll , updating the page control should be done manually to ⚠️ avoid the flickering dots when you tap on the page control.如果使用scrollViewDidScroll ,则应手动更新页面控件以 ⚠️避免点击页面控件时出现闪烁的点

Setup the UIPageControl .设置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)

Implement the action (using the extensions below).实施操作(使用下面的扩展)。

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

Update UIPageControl manually on every scroll.在每次滚动时手动更新UIPageControl

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

Convinient UICollectionView extensions. 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
        )
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM