Home > database >  Why is setViewControllers on UIPageViewController not working?
Why is setViewControllers on UIPageViewController not working?

Time:03-04

I am designing a UIPageViewController in which a user can swipe through various YouTube videos. However, I only want to load a certain amount of videoIDs, video identifiers, at a time. When the user reaches either end of an array containing the videoIDs, I make a request for more to load. However, since no view controller can be made and returned via the UIPageViewController's dataSource, in this case it is itself, nothing will happen when the user swipes again---even after more videoIDs have been added to the array (due to the dataSource already having returned nil and it does not check twice). Note: The dataSource methods mark the controller if there is no view controller after/before.

As a solution, I manually add a UIPanGestureRecognizer that:

  1. Checks if the current viewController has been marked
  2. Checks if the user has swiped in the correct direction according to the viewControllers marking
  3. Checks if another view controller can be made by manually calling one of the two dataSource methods

If all of these are true, I call setViewControllers to manually navigate. However, it does not do anything.

Requesting more data works properly and the dataSource returns another viewController when manually called however the setViewControllers method do not work.

// Add the GestureRecognizer
    private func addCustomGestureRecognizer() {
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(swipeManually))
        panGesture.delegate = self
        view.addGestureRecognizer(panGesture)

    }
    
//Triggers every time a swipe is made
    @objc private func swipeManually(_ sender: UIPanGestureRecognizer) {
        if let currentVC = self.viewControllers?.first as? LearnVideoViewController {
            guard currentVC.isEndOfTailSequence || currentVC.isEndOfHeadSequence else {return}
            let userDidSwipeUp = sender.velocity(in: view).y < 0
            
// isEndOfTailSequence means it is the last viewController
// isEndOfHeadSequence means it is the first viewController

            if currentVC.isEndOfTailSequence && userDidSwipeUp {
                swipeUpManually(on: currentVC)
            } else if currentVC.isEndOfHeadSequence && !userDidSwipeUp {
                swipeDownManually(on: currentVC)
            }
            
        }
    }


//Extension to manually navigate pages when needed
extension UIPageViewController {
    func swipeUpManually(on currentVC: LearnVideoViewController) {
        if let nextViewController = dataSource!.pageViewController( self, viewControllerAfter: currentVC) {
//No transition happens even if put in a DispatchQueue.Main.async block
            setViewControllers([nextViewController], direction: .forward, animated: true, completion: nil)
        }
    }
    
    
    func swipeDownManually(on currentVC: LearnVideoViewController) {
        if let beforeController = dataSource!.pageViewController( self, viewControllerBefore: currentVC) {
//No transition happens even if put in a DispatchQueue.Main.async block
            setViewControllers([beforeController], direction: .reverse, animated: true, completion: nil)
        }
    }
    
}

CodePudding user response:

Your problem is that setViewControllers will reset the entire stack of view controllers to whatever array of view controllers you pass in. You want to append view controllers (and possibly remove old ones), so adjust accordingly.

setViewControllers([beforeController], direction: .reverse, animated: true, completion: nil)

This tells the controller to make a view controller stack of 1 (beforeController). Get the current set and make sure to include it in this method call.

  • Related