【问题标题】:Get scroll position of UIPageViewController获取 UIPageViewController 的滚动位置
【发布时间】:2015-01-30 17:26:08
【问题描述】:

我正在使用UIPageViewController,我需要在用户滑动时获取 ViewController 的滚动位置,以便在视图转换到下一个 UIViewController 时部分淡化一些资产。

UIPageViewController 的委托和数据源方法似乎不提供对此的任何访问,并且在内部我假设UIPageViewController 必须在某处使用滚动视图,但它似乎并不直接子类化它,所以我无法调用

func scrollViewDidScroll(scrollView: UIScrollView) {

}

我看到其他一些帖子建议获取对pageViewController!.view.subviews 的引用,然后第一个索引是滚动视图,但这似乎非常 hacky。我想知道是否有更标准的方法来处理这个问题。

【问题讨论】:

  • 我能问一下你为什么要使用页面浏览控制器吗?一个简单的滚动视图会起作用吗?
  • 我通常发现 UIPageViewController 在功能和自定义方面都极其缺乏。我几乎总是在集合视图上使用滚动视图。
  • @Aggressor,我正在使用 pageViewController,因为它为我们正在构建的东西提供了近乎精确的内置功能,我可以在 UIScrollView 中编写所有代码,但我希望 pageViewController 会很健壮足以提供这个相对简单的功能,但看起来它可能没有。
  • 它具体提供了一般滚动视图中缺少的什么?正如 AdamPro13 上面提到的,我也总是使用自己的滚动视图。

标签: ios swift


【解决方案1】:

您可以在UIPageViewController 中搜索 UIScrollView。为此,您必须实现UIScrollViewDelegate

之后你就可以得到你的scrollView了:

for v in pageViewController.view.subviews{
    if v.isKindOfClass(UIScrollView){
        (v as UIScrollView).delegate = self
    }
}

之后,您可以使用所有 UIScrollViewDelegate 方法,因此您可以覆盖 scrollViewDidScroll 方法,您可以在其中获取 scrollPosition:

func scrollViewDidScroll(scrollView: UIScrollView) {
   //your Code
}

或者如果您想要单线

let scrollView = view.subviews.filter { $0 is UIScrollView }.first as! UIScrollView
scrollView.delegate = self

【讨论】:

  • 感谢您实际回答给定的问题。我会试试这个,看看它是如何工作的。 +1
  • 完美运行。谢谢
【解决方案2】:

UIPageViewController 滚动不像普通的滚动视图那样工作,你不能像其他滚动视图一样获得 scrollView.contentOffset。

所以这里有一个技巧来了解用户滚动时发生的事情:

首先你必须找到滚动视图并将委托设置为当前视图控制器,就像其他答案所说的那样。

class YourViewController : UIPageViewController {

    var startOffset = CGFloat(0) //define this

    override func viewDidLoad() {
         super.viewDidLoad()

         //from other answers   
         for v in view.subviews{
             if v is UIScrollView {
                 (v as! UIScrollView).delegate = self
             }
         }
     }

    .
    .
    .
}

extension YourViewController : UIScrollViewDelegate{

    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {

        startOffset = scrollView.contentOffset.x
    }

    public func scrollViewDidScroll(_ scrollView: UIScrollView) {

        var direction = 0 //scroll stopped

        if startOffset < scrollView.contentOffset.x {
            direction = 1 //going right
        }else if startOffset > scrollView.contentOffset.x {
            direction = -1 //going left
        }

        let positionFromStartOfCurrentPage = abs(startOffset - scrollView.contentOffset.x)
        let percent = positionFromStartOfCurrentPage /  self.view.frame.width

        //you can decide what to do with scroll
    }

}

【讨论】:

  • 这真是太棒了!
  • 好伙伴!拯救了我的一天。
  • 这仅在您不使用 2 根手指以继续在页面之间滚动时才有效。通过第二页后,此 startOffset 应设置为 0,但事实并非如此
  • @nonolays 每次 PageViewController 请求视图控制器时将 startOffset 设置为零怎么样?
  • 此代码是应用于 PageViewController 还是应用于 PageViewController 'set' 中的单个视图?
【解决方案3】:

与 Christian 的回答类似,但更像 Swift(而不是不必要地继续遍历 view.subviews):

for view in self.view.subviews {
    if let view = view as? UIScrollView {
        view.delegate = self
        break
    }
}

【讨论】:

    【解决方案4】:
    var pageViewController: PageViewController? {
        didSet {
            pageViewController?.dataSource = self
            pageViewController?.delegate = self
            scrollView?.delegate = self
        }
    }
    
    lazy var scrollView: UIScrollView? = {
        for subview in pageViewController?.view?.subviews ?? [] {
            if let scrollView = subview as? UIScrollView {
                return scrollView
            }
        }
        return nil
    }()
    
    extension BaseFeedViewController: UIScrollViewDelegate {
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
    
        let offset = scrollView.contentOffset.x
        let bounds = scrollView.bounds.width
        let page = CGFloat(self.currentPage)
        let count = CGFloat(viewControllers.count)
        let percentage = (offset - bounds + page * bounds) / (count * bounds - bounds)
    
        print(abs(percentage))
    }
    }
    

    【讨论】:

      【解决方案5】:

      从 iOS 13 开始,UIPageViewController 在转换到另一个视图控制器后似乎会重置滚动视图的 contentOffset。这是一个可行的解决方案:

      1. 按照其他答案的建议,找到子 scrollView 并将其委托设置为 self
      2. 跟踪pageViewController的当前页面索引:
      var currentPageIndex = 0
      // The pageViewController's viewControllers
      let orderredViewControllers: [UIViewController] = [controller1, controller2, ...]
      
      pageViewController.delegate = self
      
      func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
          guard completed, let currentViewController = pageViewController.viewControllers?.first else { return }
          currentPageIndex = orderredViewControllers.firstIndex(of: currentViewController)!
      }
      
      1. 获取0到1的进度
      func scrollViewDidScroll(_ scrollView: UIScrollView) {
          let contentOffsetX = scrollView.contentOffset.x
          let width = scrollView.frame.size.width
          let offset = CGFloat(currentPageIndex) / CGFloat(orderredViewControllers.count - 1)
          let progress = (contentOffsetX - width) / width + offset
      }
      

      【讨论】:

        【解决方案6】:

        为了使代码尽可能可读和分离,我会在UIPageViewController上定义一个扩展:

        extension UIPageViewController {
          var scrollView: UIScrollView? {
            view.subviews.first(where: { $0 is UIScrollView }) as? UIScrollView
          }
        }
        

        将自己设置为滚动视图事件的delegate 非常容易,如下所示:

        pageViewController.scrollView?.delegate = self
        

        【讨论】:

          猜你喜欢
          • 2011-04-08
          • 1970-01-01
          • 2023-01-10
          • 1970-01-01
          • 2017-08-01
          • 2019-12-18
          • 2014-02-23
          • 2012-03-18
          • 2015-10-21
          相关资源
          最近更新 更多