0Day Forums
Get scroll position of UIPageViewController - Printable Version

+- 0Day Forums (https://0day.red)
+-- Forum: Coding (https://0day.red/Forum-Coding)
+--- Forum: Swift (https://0day.red/Forum-Swift)
+--- Thread: Get scroll position of UIPageViewController (/Thread-Get-scroll-position-of-UIPageViewController)



Get scroll position of UIPageViewController - ultrasound615963 - 07-19-2023

I am using a `UIPageViewController`, and I need to get the scroll position of the ViewController as the users swipe so I can partially fade some assets while the view is transitioning to the next `UIViewController`.

The delegate and datasource methods of `UIPageViewController` don't seem to provide any access to this, and internally I'm assuming that the `UIPageViewController` must be using a scroll view somewhere, but it doesn't seem to directly subclass it so I'm not able to call

func scrollViewDidScroll(scrollView: UIScrollView) {

}

I've seen some other posts suggestion to grab a reference to the `pageViewController!.view.subviews` and then the first index is a scrollView, but this seems *very* hacky. I'm wondering if there is a more standard way to handle this.


RE: Get scroll position of UIPageViewController - martinelli55 - 07-19-2023

As of iOS 13, the `UIPageViewController` seems to reset the scrollview's contentOffset once it transitions to another view controller. Here is a working solution:

1. Find the child scrollView and set its delegate to self, as other answers suggested
2. Keep track of the current page index of the 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)!
}
```

3. Get the progress that ranges from 0 to 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
}
```


RE: Get scroll position of UIPageViewController - deposition168 - 07-19-2023

To make the code as readable and separated as possible, I would define an extension on `UIPageViewController`:

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

It's quite easy to set yourself as the `delegate` for scroll view events, as so:
```
pageViewController.scrollView?.delegate = self
```



RE: Get scroll position of UIPageViewController - consenting460514 - 07-19-2023


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


RE: Get scroll position of UIPageViewController - Madelaine512575 - 07-19-2023

UIPageViewController scroll doesn't work like normal scrollview and you can't get scrollView.contentOffset like other scrollViews.

so here is a trick to get what's going on when user scrolls :

first you have to find scrollview and set delegate to current viewController like other answers said.

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
}

}


RE: Get scroll position of UIPageViewController - insufficient677 - 07-19-2023

You can search for the UIScrollView inside your `UIPageViewController`. To do that, you will have to implement the `UIScrollViewDelegate`.

After that you can get your scrollView:

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

After that, you are able to use all the UIScrollViewDelegate-methods and so you can override the `scrollViewDidScroll` method where you can get the scrollPosition:

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

---

Or if you want a **one-liner**:

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


RE: Get scroll position of UIPageViewController - proportioned568302 - 07-19-2023

Similar to Christian's answer but a bit more Swift-like (and not unnecessarily continuing to loop through view.subviews):

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