Preload next page in UIPageViewController
Asked Answered
B

1

6

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.

Brenner answered 26/2, 2013 at 17:24 Comment(3)
Did you find a solution for this? I have the same problem, getting noticeable lag when swiping to the next view controller.Meanwhile
Sort of, but the answer is very specific to my implementation. As I've worked with UIPageViewController a lot since posting this question, I've found it's a very tricky subject. Basically, my issues had more to do with querying the DB for the right data to show, which was slowing each page down. I was able to optimize my page turns by pre-calculating data from the DB, and drawing my views using Auto Layout instead of messing with frames - that helped a lot. Secondly I had lots of images to show, so I stored them in an NSMutableArray directly on the "main" view controller (parent of swipe).Brenner
My "solution" ended up being...keep waiting for Apple to come out with faster phones....hahaha. On iPhone 6/6s, there is no lag anymore. I optimized some of the database fetching code to happen once when the page controller is first created and that helped a lot, but the main speed improvements have come from faster processors and more RAM, and phasing out support for iPhone 4/4s =)Brenner
I
4

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().

Ingot answered 30/7, 2015 at 9:27 Comment(3)
How does this work if you don't have a fixed amount of VC's? Is there a way to preload all of them at once so there are no issues? Thanks!Channing
@Jacob: If you don't have a fixed amount of VCs, then you will first have to check if there are VCs before and after the selected VC. If there is only a VC after it, you will only use the code in viewDidLoad(), and viewWillLayoutSubviews() will not be used. If there is only a VC before it, you will use the code in viewWillLayoutSubviews(), and everything in viewWillLoad starting with the //Load to the viewController after the starting VC... comment will be removed. You may be able to go to the previous VC in viewDidLoad() in this case. I'm not sure how to preload all VCs, sorry.Ingot
hello can you provide the full code from initializing pageViewController to finalize itMcclintock

© 2022 - 2024 — McMap. All rights reserved.