Refresh UIPageViewController - reorder pages and add new pages

29,003

Solution 1

You need to call setViewControllers:direction:animated:completion:.

Also, in iOS 6, watch out if you're using UIPageViewControllerTransitionStyleScroll style, as there is a major caching bug if animated: is YES (see my discussion here: UIPageViewController navigates to wrong page with Scroll transition style).

Solution 2

I found a workaround to force UIPageViewController to forget about cached view controllers of neighboring pages that are currently not displayed:

pageViewController.dataSource = nil;
pageViewController.dataSource = self;

I do this everytime I change the set of pages. Of course this doesn't affect the currently displayed page.

With this workaround I avoid the caching bug and can still use animated:YES in setViewControllers:direction:animated:completion:.

Solution 3

Here's my complete implementation of @ortwin-gentz's answer in the didFinish delegate method in Swift 2, which works perfectly for me:

func pageViewController(
    pageViewController: UIPageViewController,
    didFinishAnimating finished: Bool,
    previousViewControllers: [UIViewController],
    transitionCompleted completed: Bool
) {
    if !completed { return }
    dispatch_async(dispatch_get_main_queue()) {
        pageViewController.dataSource = nil
        pageViewController.dataSource = self
    }
}

Solution 4

Swift 4 Version for reload of data of pageviewcontroller, to @theory's version.

func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
    if !completed { return }
    DispatchQueue.main.async() {
        self.dataSource = nil
        self.dataSource = self
    }
}
Share:
29,003
ssloan
Author by

ssloan

Updated on July 09, 2022

Comments

  • ssloan
    ssloan almost 2 years

    I have a UIPageViewController which I am providing page data for using an implementation of UIPageControllerDelegate and UIPageControllerDataSource.

    It's all working fine, but I want to be able to add items to the page data and reorder the page data.

    If a user has already got to the last of the pages, and then I add an item, they can't get to the next page because viewControllerAfterViewController: has already been called. If they scroll back one and then forward two they can get to the new page fine, so the data is setup correctly. How can I tell the UIPageViewController to refresh its store of what comes next?

    Similarly I would like to reorder the collection that is backing the page view. But if I do this I'll get the same problem - the page view will think the next page is still what it was last time the current page was loaded.

    I guess I'm looking for something similar to reloadData: on UITableView.

  • ssloan
    ssloan about 11 years
    Thanks - the setViewControllers:direction:animated:completion: did it. Cheers for mentioning the bug too.
  • ernesto
    ernesto about 10 years
    Interesting. Where do you unset and reset the dataSource? Before setViewControllers:direction:animated:completion:, after it, or in the completion block? Thanks.
  • Ortwin Gentz
    Ortwin Gentz about 10 years
    Actually, I do this 0.01 seconds after didFinish in a dispatch_async block.
  • Michael McGuire
    Michael McGuire over 9 years
    Thank you! This method works when using a data source. The accepted answer didn't work for me, at least on iOS 8. Using "setViewControllers:direction:animated:completion:" with the current view controller did not cause the data source to be called again.
  • Alex Sfinx87
    Alex Sfinx87 almost 9 years
    Thx for this solution. :-)
  • Mr.Fingers
    Mr.Fingers about 8 years
    tip: After you reset dataSource (a.k.a. pageViewController.dataSource = nil;) your method -viewControllerBefore and -viewControllerAfter will be called again. For restore the dataSource.
  • Ozgur Vatansever
    Ozgur Vatansever about 8 years
    You need to reassign dataSource like above if presentationCountForPageViewController returns a dynamic value which might change after the refresh.
  • BaseZen
    BaseZen over 7 years
    Double plus good. Why this is not documented -- inexcusable. Confirmed for iOS 10, Swift 2.3, Xcode 8
  • Kartick Vaddadi
    Kartick Vaddadi about 7 years
    This doesn't work, at least not in iOS 10.3. The crash happens in setViewControllers(), so doing something 0.01 seconds afterward is too late. I tried this trick before calling setViewControllers(), and it doesn't work.
  • Gon
    Gon almost 7 years
    doesn't work iOS 10 when you call setViewControllers:direction:animated:completion: fast enough, still got wrong content offset.
  • Uday Kumar Eega
    Uday Kumar Eega about 6 years
    Worked like charm...Perfect..Thanks
  • DJTano
    DJTano over 3 years
    And what if I also want to update the currently displayed page ?
  • Ortwin Gentz
    Ortwin Gentz over 3 years
    @DJTano that's the purpose of setViewControllers:direction:animated:completion:. Even though it asks for an array of view controllers, you usually send a single array (exception is a 2-element array if spineLocation = .mid) with the current VC.
  • DJTano
    DJTano over 3 years
    @OrtwinGentz got it. Thanks!! :)