I need a way to make UIPageViewController reuse controllers to save memory, because I have a huge number of pages to show! I did the basic implementation of the UIPageViewController but couldn't manage to make controller reusable, please advice!
2 Answers
To solve this problem in my current project, I cache all view controllers that are created as pages for the UIPageViewController
in a Set
. Whenever the UIPageViewController
requests a new view controller from its data source, I filter out an unused from that cache by checking the parentViewController
property; if no unused view controller is available, a new one is created.
My setup of the UIPageViewController
and the cache looks similar to this:
class MyPageViewController: UIPageViewController {
private var reusableViewControllers = Set<MyViewController>()
init() {
super.init(/* ... */)
self.dataSource = self
let initialViewController = self.unusedViewController()
// Configure the initial view controller
// ...
self.setViewControllers([ initialViewController ],
direction: .Forward,
animated: false,
completion: nil)
}
// This function returns an unused view controller from the cache
// or creates and returns a new one
private func unusedViewController() -> MyViewController {
let unusedViewControllers = reusableViewControllers.filter { $0.parentViewController == nil }
if let someUnusedViewController = unusedViewControllers.last {
return someUnusedViewController
} else {
let newViewController = MyViewController()
reusableViewControllers.insert(newViewController)
return newViewController
}
}
}
The data source uses the same function to obtain an unused view controller:
extension MyPageViewController: UIPageViewControllerDataSource {
func pageViewController(pageViewController: UIPageViewController,
viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
let nextViewController = unusedViewController()
// Configure the next view controller for display
// ...
return nextViewController
}
}

- 3,826
- 30
- 59
-
Is this currently the best way to horizontally swipe through records in a database (one record per page)? In Android, it's kind of already done as a `ViewPager`, and it manages the re-use for you. – Dale Jun 05 '18 at 15:01
-
1Thanks for this. I'm using ```if let someUnusedViewController = reuseableViewControllers.first(where: { $0.parentViewController == nil }) {``` which is possibly slightly more efficient. I'm using an array not a Set. I find on iOS 12.1 that the code builds only three view controllers. – PhoneyDeveloper Mar 01 '19 at 17:52
-
1This is a very clever solution. Although I have two questions, if using set how do you figure out the index of which page you are on to avoid creating a carousel effect. Also as a general question what is the advantage to using a UIPageViewController over a full sized collectionViewCell in this circumstance? – TheRedCamaro3.0 3.0 Apr 08 '19 at 05:43
-
You need to track indices separately, for example, by downcasting `viewController` to a custom `UIViewController` subclass that has an `index` property. Your second question is too broad to answer in a comment. You might want to read the documentation for `UIPageViewController` and `UICollectionView` first, and then post a new, more specific question if anything is still unclear. – Theo Apr 09 '19 at 10:50
-
2Thanks. May I know, how do you retain UI state in UIPageViewController? For instance, in 2nd page, it has UICollectionView of scroll position X. When we swipe back to 2nd page, how can we restore the X position of UICollectionView? – Cheok Yan Cheng Jul 22 '19 at 06:05
You could use ACReuseQueue to achieve what you want. It provides a queue for reusing your view controllers. You can use the UIPageViewController data source methods to dequeue a VC from the reuse queue. The tricky part is to know when to put them back in the reuse queue. UIPageViewController does not provide any method to know when this happens, but there is a way. UIPageViewController is a container VC that adds and removes its child view controllers using the VC containment APIs. Your VC will receive didMoveToParentViewController: with nil as the argument if it is being removed from the UIPageViewController. Use this method to add yourself back to the queue.

- 71
- 1
-
"The tricky part is to know when to put them back in the reuse queue. UIPageViewController does not provide any method to know when this happens, but there is a way." This finally made it click, thanks! – nolanw May 17 '19 at 18:41