Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preload next page in UIPageViewController

I've looked many, many places and have yet to find some good sample code showing how to pre-load the "next" page in a UIPageViewController. There are a few answers on SO detailing some theoretical ways to do it (see this question) but no one has yet to post a working example.

In the workflow of my app I'm showing 1 page per screen and I want to have the "next" screen preloaded because as it is, swiping to the next page can be very slow, sometimes requiring 2 swipes (if you swipe too fast) in order for the next page to be rendered and shown. This provides a bad user experience. I don't really care about preloading the "previous" or any other screens as the typical workflow will be that users stay on a screen for a while before moving to the next screen (to the right). I'm using the slide animation (not curl up). I create all views programatically and do not use IB at all.

I've tried to store some UIViewControllers in an NSMutableArray and load the controllers from there, but it's tricky to get working right and didn't seem to speed anything up. There must be a good way to do this.

Any help is greatly appreciated.

like image 211
DiscDev Avatar asked Feb 26 '13 17:02

DiscDev


1 Answers

I've solved for my case in a somewhat of hack. For each ContentView, I have a UIImageView within a UIScrollView for zooming. My problem was that upon booting up the app, if the user zoomed before swiping, going to the next page while zoomed in wouldn't work too well. I use the following code (Swift 1.2) to solve this problem. As I've said, it's a bit of a hack though.

var layoutsubs = false

override func viewDidLoad() {
    super.viewDidLoad()
    //Other code for implementing pageViewController omitted

    //add pageViewController to main view
    self.addChildViewController(pageViewController)
    self.view.addSubview(pageViewController.view)
    pageViewController.didMoveToParentViewController(self)

    //Load to the viewController after the starting VC, then go back to the starting VC
    var viewControllers = [afterVC]
    pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Forward, animated: true, completion: nil)
    viewControllers = [startingVC]
    pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Reverse, animated: true, completion: nil)
}

override func viewWillLayoutSubviews() {
    //Load the viewController before the starting VC then go back to the starting VC
    //viewWillLayoutSubviews() is called multiple times, so do this only once
    if !layoutsubs {
        let startingVC = self.viewControllerAtIndex(imageIndex) as ContentViewController
        let beforeVC = pageViewController(pageViewController, viewControllerBeforeViewController: startingVC) as! ContentViewController

        var viewControllers = [beforeVC]
        pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Reverse, animated: true, completion: nil)
        viewControllers = [startingVC]
        pageViewController.setViewControllers(viewControllers as [AnyObject], direction: .Forward, animated: true, completion: nil)
        layoutsubs = true
    }
}

Essentially, I load the view controllers before and after the starting view controller. I do this by setting each one to the VC to see via setViewControllers(_:direction:animated:completion:) (see ref), then by going back to the starting view controller. Why is this in two different functions? Well, if you put it all in one, only one of the two view controllers next to the starting VC will load. This may be desirable for some cases, but I needed all three VCs (before, starting, and after) to load.

I'm not sure how well this method would work if the UIPageViewController was already loaded. For instance, if you needed to load the page 2 away from the page being viewed, after a few swipes. It may skip around if you put it in willTransitionToViewControllers().

like image 195
natec Avatar answered Nov 18 '22 11:11

natec