检测 UIScrollView 页面变化

发布于 2024-10-21 21:02:50 字数 50 浏览 5 评论 0原文

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

Is there a way to detect or get a notification when user changes the page in a paging-enabled UIScrollView?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

日久见人心 2024-10-28 21:02:50

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

- (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: 方法。

Use this to detect which page is currently being shown and perform some action on page change:

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

If it's acceptable for you to only react to the page change once the scrolling has completely stopped, then it would be best to do the above inside the scrollViewDidEndDecelerating: delegate method instead of the scrollViewDidScroll: method.

沙沙粒小 2024-10-28 21:02:50

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

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

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

In paging enabled scroll view you can use scrollViewDidEndDecelerating to know when the view is settled on a page (might be the same page).

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView

scrollViewDidScroll gets called on every movement. And in context of paging enabled view can be used to find when it is scrolled enough to move to next page (if dragging is stopped at that point).

感受沵的脚步 2024-10-28 21:02:50

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

How about combining two methods of UIScrollViewDelegate?

In scrollViewDidEndDragging(_:willDecelerate:), if it stops right away, we do the page calculation; if it is decelerating, we let it go and it will be caught by scrollViewDidEndDecelerating(_:).

The code is tested with XCode version 7.1.1, Swift version 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
   }
}
会傲 2024-10-28 21:02:50

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

我不想仅仅为了跟踪页码而调用 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;
}

这样用户一次只能滑动一页,而不会出现上述风险。

First result on google for page detection so I had to answer this with a better solution in my opinion. (even if this question was asked 2 and a half years ago.)

I'd prefer not to call scrollViewDidScroll just to track the page number. Thats an overkill for something simple as that.
Using scrollViewDidEndDecelerating does work and stops on a page change BUT (and it's a big but) if the user will swipe on the screen twice a bit faster than normal scrollViewDidEndDecelerating will be called only once. You can easily go from page #1 to page #3 without processing page #2.

This solved it completely for me:

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

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

That way the user can only swipe one page at a time without the risk described above.

独﹏钓一江月 2024-10-28 21:02:50

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


}

Swift 4

I found the best way to do this is by using scrollViewWillEndDragging(_:withVelocity:targetContentOffset:). It lets you predict if paging will occur as soon as you lift your finger off the screen. This example is for paging horizontally.

Remember to the assign the scrollView.delegate to the object that adopts UIScrollViewDelegate and implements this method.

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)


}
沉溺在你眼里的海 2024-10-28 21:02:50

实现UIScrollView的委托。 这个方法就是您正在寻找的。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView

Implement the delegate of UIScrollView. This method is what you are looking for.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
铁轨上的流浪者 2024-10-28 21:02:50

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

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

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

然后在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
           }
       }
   }

Here is the swift solution for this.

Make two properties currentPage and previousPage in the class where you are implementing your code and initialize them to 0.

Now update currentPage from scrollViewDidEndDragging(:willDecelerate:)
and scrollViewDidEndDecelerating(
:scrollView:).

And then update previousPage in scrollViewDidEndScrollingAnimation(_:scrollView:)

    //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
           }
       }
   }
唔猫 2024-10-28 21:02:50
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
    }
}
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)")
    }
}

And Use extension

extension UIScrollView {
    var currentPage: Int {
        return Int((self.contentOffset.x + (0.5 * self.frame.size.width)) / 
        self.frame.width) + 1
    }
}
拥抱影子 2024-10-28 21:02:50

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 页打开“书”,则在您的 boss 类中,您将调用该函数以将其设置为“17”带来。

在这样的示例中,您只需记住必须在任何此类启动功能;没有真正通用的方法来处理起始情况。

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

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

Cut and paste for 2019

It's not so easy to do this:

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

Works perfectly.

pageHasActuallyChanged will only be called when the user changes pages in what humans would consider "changing pages".

Be aware: the bringup is tricky:

This is difficult to initialize at bringup time, and will depend on how you are using the paged system.

In any paged system you will very likely have something like "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
    }
}

So, if the user opens the "book" at page 17, in your boss class you'd be calling that function to set it to "17" on bringup.

In such an example, you'd just have to remember that you must set initially our possibleQuantumPage value in any such bringup functions; there's no really generalized way to handle the starting situation.

After all you may, just for example, want to "quickly scroll" to the bringup page, and, who knows what that "means" in a quantumPage situation. So, be sure to initialize your quantum page system carefully during bringup, based on your situation.

In any event, just copy and paste the five functions at the top to get perfect quantum paging.

空城旧梦 2024-10-28 21:02:50

对于斯威夫特

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

For Swift

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
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文