获取UIPageViewController的滚动位置

时间:2015-01-30 17:26:08

标签: ios swift

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

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

func scrollViewDidScroll(scrollView: UIScrollView) {

}

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

6 个答案:

答案 0 :(得分:35)

您可以在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 :(得分:6)

类似于克里斯蒂安的回答,但有点像Swift一样(并且没有不必要地继续循环浏览view.subviews):

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

答案 2 :(得分:4)

UIPageViewController滚动无法像普通的scrollview一样工作,并且您无法像其他scrollViews一样获得scrollView.contentOffset。

因此,这是一个技巧,可以了解用户滚动时发生的情况:

首先,您必须找到scrollview并将委托设置为当前的viewController,就像其他答案所说的那样。

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
    }

}

答案 3 :(得分:1)

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

答案 4 :(得分:0)

为了使代码尽可能易读和分离,我将在UIPageViewController上定义扩展名:

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

将自己设置为滚动视图事件的delegate很容易,就像这样:

pageViewController.scrollView?.delegate = self

答案 5 :(得分:0)

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