获取 UIPageViewController 的滚动位置
Posted
技术标签:
【中文标题】获取 UIPageViewController 的滚动位置【英文标题】:Get scroll position of 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 上面提到的,我也总是使用自己的滚动视图。 【参考方案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。这是一个可行的解决方案:
-
按照其他答案的建议,找到子 scrollView 并将其委托设置为 self
跟踪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)!
-
获取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
【讨论】:
以上是关于获取 UIPageViewController 的滚动位置的主要内容,如果未能解决你的问题,请参考以下文章
在 UIPageViewController 中获取用户滑动方向
如何在 UIPageViewController 中获取当前控制器
我能以某种方式从 UIPageViewController 获取 scrollViewDidScroll: UIScrollView Delegate 方法吗?