13

I can't find a good solution to having the UIPageViewController preload the surrounding ViewControllers.

Right now what happens is when the user starts to turn the page, nothing happens, the code calls

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController
       viewControllerAfterViewController:(UIViewController *)viewController

and once its loaded thennnn it shows the animation. This takes too long and is not smooth.

Anyone have a solution?

Eric
  • 5,671
  • 5
  • 31
  • 42

1 Answers1

19

Create a mutable container and when you show the first view, create the two viewController that would be needed if the user pages, once you have the viewController object ask it for its view (to get it to load the nib and call the controller's "viewDidLoad:" method. You need to figure out a system on identifying the viewControlers so you can retrieve the viewController you need). You might be able to do the heavy lifting here in a block on a dispatch_queue.

From then on, when the user pages, you look first in the container for the viewController, and if not found, you have to do it in real time.

You probably want to use a dispatch_group so that you can wait on it for pending blocks to finish before paging.

Everytime the user pages, you will look and see if the pages surrounding that page are in the container or not. You could also pre-fetch more viewControllers - like two forward two reverse at each page shown.

David H
  • 40,852
  • 12
  • 92
  • 138
  • 2
    I basically have this implemented, and I have the call to ask for the controllers view (to call viewdidload), but it still has a significant delay from when the user starts to scroll and from when it displays the controller. Its as if its not really loaded, just partially – Eric Jul 25 '12 at 23:20
  • So, do you have all your "heavy lifting" done in viewDidLoad, or do you have some in viewWillAppear? I am using this technique with great success... – David H Jul 26 '12 at 00:20
  • The only thing my content view controller does is in viewdidload it sets the UIImageView (which is full screen) to an image. It doesnt do any "heavy lifting" anywhere. – Eric Jul 26 '12 at 00:51
  • So you then have some problem you don't know about. If the "new" viewController only does some minimal amount of work. why the delay? I use this feature with no problems, yet my viewControllers have a lot of compelexity, must load many images and complex text items. Something is amiss here. – David H Jul 26 '12 at 01:10
  • My workflow is as follows. The app starts with one photo displayed. The user then presses a button to open the camera, this image is returned to controller, I create and initialize a new contentviewcontroller with this image, I then add this controller to a mutablearray. Now in pageViewController:viewControllerAfterViewController: I simply find and return the correct content controller (the one just created for example). Every flip after it loads once is fine, its just that initial time... – Eric Jul 26 '12 at 01:37
  • @DavidH Any chance you have some sample code on how you do this? Have somewhat complicated View controllers, and would love to be able to scroll through them at a very high pace (or rather, for my users to do that), but can't quite get it optimized to my liking. Curious how you are using dispatch_group. – Bob Spryn Jun 08 '13 at 07:47
  • @BobSpryn Dispatch group provides a way to wait until all group blocks complete. You now mentioned the camera, and there are system delays with it you can do nothing about. My point on the vcs is that you can often do with UI elements what you do with strings etc - that is put them in mutable containers for future use. – David H Jun 08 '13 at 11:29
  • Camera? I don't think I mentioned that here. No I'm not showing the camera in this instance. Rather I'm asking if you are somehow rendering view controllers in a background thread somehow, and curious what that looks like. – Bob Spryn Jun 08 '13 at 15:56
  • I am having this exact problem, and my issues is that the content of my view controllers is a UICollectionView, and I fail to cache the cells here. When I initially swipe to the next view controller `cellForItemAtIndexPath:` is called and creates a lag. Any suggestions for solving this? – mattsson Feb 20 '14 at 09:36
  • @mattsson create a method to tell those classes to create and cache the cells, and another to finally release them. Perhaps you can get that to work. If not the cells then the content they eventually need ie images, attributed text, and put those in an NSCache. – David H Feb 20 '14 at 11:59
  • @DavidH This is a very elegant solution, but can you provide a little more guidance regarding the use of a dispatch_queue please? Won't this be UI work on a background thread? – Ben Packard Jun 18 '14 at 12:54
  • 2
    @BenPackard what most people don't understand is that if UI is showing - it has a window property - all changes must be done on the main thread. However, if you are just constructing or loading a UI element, and its not showing, its usually safe to load it from a nib or otherwise populate it. You can create a new serial queue and associate it with the background thread, and once the view is setup post a block to the main thread with the newly created and configured view. You can find a huge amount of info on GCD, dispatch queues, etc on the Apple site and here. – David H Jun 18 '14 at 15:28
  • @DavidH In a for loop of my 3 views, I scroll to the first and `print(someViewController.view)` to pre-load the other 2. Doesn't feel right, but works a charm. – Patrick Aug 22 '16 at 23:09
  • @Patrick try just putting "someViewController.view" on a line by itself. – David H Aug 23 '16 at 01:28