81

当用户在启用分页的 UIScrollView 中更改页面时,有没有办法检测或获取通知?

4

10 回答 10

194

使用它来检测当前正在显示哪个页面并在页面更改时执行一些操作:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    static NSInteger previousPage = 0;
    CGFloat pageWidth = scrollView.frame.size.width;
    float fractionalPage = scrollView.contentOffset.x / pageWidth;
    NSInteger page = lround(fractionalPage);
    if (previousPage != page) {
        // Page has changed, do your thing!
        // ...
        // Finally, update previous page
        previousPage = page;
    }
}

如果您可以在滚动完全停止后仅对页面更改做出反应,那么最好在scrollViewDidEndDecelerating:委托方法而不是scrollViewDidScroll:方法中执行上述操作。

于 2011-03-11T11:32:49.450 回答
65

在启用分页的滚动视图中,您可以使用scrollViewDidEndDecelerating来了解视图何时在页面上确定(可能是同一页面)。

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

scrollViewDidScroll每个动作都会被调用。在启用分页的上下文中,可以使用视图来查找它何时滚动到足以移动到下一页(如果在该点停止拖动)。

于 2012-04-07T16:01:55.597 回答
45

结合 UIScrollViewDelegate 的两种方法怎么样?

scrollViewDidEndDragging(_:willDecelerate:)中,如果它立即停止,我们进行页面计算;如果它正在减速,我们让它走,它会被scrollViewDidEndDecelerating(_:).

代码使用 XCode 7.1.1 版、Swift 2.1 版测试

class ViewController: UIViewController, UIScrollViewDelegate {

  // MARK: UIScrollViewDelegate
  func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if decelerate == false {
        let currentPage = scrollView.currentPage
        // Do something with your page update
        print("scrollViewDidEndDragging: \(currentPage)")
    }
  }

  func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
    let currentPage = scrollView.currentPage
    // Do something with your page update
    print("scrollViewDidEndDecelerating: \(currentPage)")
  }

}

extension UIScrollView {
   var currentPage: Int {
      return Int((self.contentOffset.x+ (0.5*self.frame.size.width))/self.frame.width)+1
   }
}
于 2015-11-19T15:33:17.670 回答
25

谷歌页面检测的第一个结果,所以我认为我必须用更好的解决方案来回答这个问题。(即使这个问题是在两年半前提出的。)

我不想打电话scrollViewDidScroll只是为了跟踪页码。对于这么简单的事情来说,这太过分了。使用scrollViewDidEndDecelerating确实有效并在页面更改时停止但是(这是一个很大的但是)如果用户在屏幕上滑动的速度比正常快两次,scrollViewDidEndDecelerating则只会调用一次。您可以轻松地从第 1 页转到第 3 页,而无需处理第 2 页。

这完全为我解决了:

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView
{
    scrollView.userInteractionEnabled = NO;
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    //Run your code on the current page
    scrollView.userInteractionEnabled = YES;
}

这样,用户一次只能滑动一页,而没有上述风险。

于 2013-12-16T12:53:42.833 回答
18

斯威夫特 4

我发现最好的方法是使用scrollViewWillEndDragging(_:withVelocity:targetContentOffset:). 它可以让您预测是否会在您将手指从屏幕上移开时立即发生分页。此示例用于水平分页。

请记住分配给scrollView.delegate采用UIScrollViewDelegate并实现此方法的对象。

var previousPageXOffset: CGFloat = 0.0

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

    let targetOffset = targetContentOffset.pointee

    if targetOffset.x == previousPageXOffset {
        // page will not change
    } else if targetOffset.x < previousPageXOffset {
        // scroll view will page left
    } else if targetOffset.x > previousPageXOffset {
        // scroll view will page right
    }

    previousPageXOffset = targetOffset.x
    // If you want to track the index of the page you are on just just divide the previousPageXOffset by the scrollView width.
    // let index = Int(previousPageXOffset / scrollView.frame.width)


}
于 2018-03-27T14:24:48.327 回答
17

实现 UIScrollView 的委托。这个方法就是你要找的。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
于 2011-03-11T11:20:25.257 回答
2

这是解决此问题的快速解决方案。

在要实现代码的类中创建两个属性 currentPage 和 previousPage 并将它们初始化为 0。

现在从 scrollViewDidEndDragging( :willDecelerate:) 和 scrollViewDidEndDecelerating( :scrollView:) 更新 currentPage。

然后在 scrollViewDidEndScrollingAnimation(_:scrollView:) 中更新 previousPage

    //Class Properties
    var currentPage = 0
    var previousPage = 0

func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        updatePage(scrollView)
        return
    }

 func scrollViewDidEndDecelerating(scrollView: UIScrollView){
        updatePage(scrollView)
        return
    }


 func updatePage(scrollView: UIScrollView) {
        let pageWidth:CGFloat = scrollView.frame.width
        let current:CGFloat = floor((scrollView.contentOffset.x-pageWidth/2)/pageWidth)+1
        currentPage = Int(current)

        if currentPage == 0 {
              // DO SOMETHING
        }
        else if currentPage == 1{
              // DO SOMETHING

        }
    }

func scrollViewDidEndScrollingAnimation(scrollView: UIScrollView) {
     if previousPage != currentPage {
          previousPage = currentPage
          if currentPage == 0 {
              //DO SOMETHING
             }else if currentPage == 1 {
               // DO SOMETHING
           }
       }
   }
于 2017-03-22T10:25:00.253 回答
1

2019 年剪切和粘贴

做到这一点并不容易:

var quantumPage: Int = -100 {   // the UNIQUELY LANDED ON, NEVER REPEATING page
    didSet {
        print(">>>>>> QUANTUM PAGE IS \(quantumPage)")
        pageHasActuallyChanged() // your function
    }
}

private var possibleQuantumPage: Int = -100 {
    didSet {
        if oldValue != possibleQuantumPage {
            quantumPage = possibleQuantumPage
        }
    }
}

public func scrollViewDidEndDragging(
                    _ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if decelerate == false {
        possibleQuantumPage = currentPageEvenIfInBetween
    }
}

public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    possibleQuantumPage = currentPageEvenIfInBetween
}

var currentPageEvenIfInBetween: Int {
   return Int((self.contentOffset.x + (0.5 * self.frame.width)) / self.frame.width)
}

完美运行。

pageHasActuallyChanged当用户更改人类认为“更改页面”的页面时才会调用。

请注意:启动很棘手:

这在启动时很难初始化,并且取决于您如何使用分页系统

在任何分页系统中,您很可能会有类似“scrollToViewAtIndex...”的内容

open func scrollToViewAtIndexForBringup(_ index: Int) {
    if index > -1 && index < childViews.count {
        
        let w = self.frame.size.width
        let h = self.frame.size.height
        
        let frame = CGRect(x: CGFloat(index)*w, y: 0, width: w, height: h)
        scrollRectToVisible(frame, animated: false) // NOTE THE FALSE
        
        // AND IMPORTANTLY:
        possibleQuantumPage = currentPageEvenIfInBetween
    }
}

因此,如果用户在第 17 页打开“书”,则在您的老板类中,您将调用该函数以在启动时将其设置为“17”。

在这样的示例中,您只需要记住,您必须在任何此类启动函数中初始设置我们的 possibleQuantumPage 值;没有真正通用的方法来处理起始情况。

毕竟,例如,您可能想要“快速滚动”到弹出页面,并且,谁知道在quantumPage 情况下这“意味着”什么。因此,请务必在启动期间根据您的情况仔细初始化您的量子页面系统。

无论如何,只需复制并粘贴顶部的五个函数即可获得完美的量子分页。

于 2019-11-26T18:13:23.457 回答
1
var scrollViewPage = 0
override func viewDidLoad() {
    super.viewDidLoad()
    scrollViewPage = scrollView.currentPage
}

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    if scrollViewPage != scrollView.currentPage {
        scrollViewPage = scrollView.currentPage
        // Do something with your page update
        print("scrollViewDidEndDecelerating: \(scrollViewPage)")
    }
}

并使用扩展

extension UIScrollView {
    var currentPage: Int {
        return Int((self.contentOffset.x + (0.5 * self.frame.size.width)) / 
        self.frame.width) + 1
    }
}
于 2020-09-11T10:28:54.673 回答
0

对于斯威夫特

static var previousPage: Int = 0
func scrollViewDidScroll(_ scrollView: UIScrollView){
    let pageWidth: CGFloat = scrollView.frame.width
    let fractionalPage: CGFloat = scrollView.contentOffset.x / pageWidth
    let page = lround(Double(fractionalPage))
    if page != previousPage{
        print(page)
        // page changed
    }
}
于 2018-10-26T12:44:46.493 回答