Search code examples
iosswiftuipageviewcontroller

iOS UIPageViewController Gets Confused and ViewControllers Are Not Displayed


We have left and right buttons set up for the user to page through different cars quickly. Our Page View Controller loses the view controller if the user taps quickly to the next page 10 or more times.

Here is the vehicle page with the car showing correctly (blurred to hide non-relevant information). See image here:

Shows Vehicle Correctly

If scrolling animation is on (true), it loses the vehicle page after tapping the right arrow 6 or more times quickly. See image here:

enter image description here

Code:

private func show(viewController:UIViewController, going direction: UIPageViewControllerNavigationDirection) {
    let viewControllers = [viewController]
    let isAnimated = true // false always works. However, animation is required.
    setViewControllers(viewControllers, direction: direction, animated: isAnimated, completion: nil)
}

While debugging and when the page view controller has stopped showing the cars, I ensured that the view controller being set is not nil and the listing (car) is also non-nil.

I tried a variant of the solution from UIPageViewController, how do I correctly jump to a specific page without messing up the order specified by the data source? where the completion block is used. However, it did not work.

weak var pvcw: UIPageViewController? = self
setViewControllers(viewControllers, direction: direction, animated: true, completion: {(_ finished: Bool) -> Void in
    let pvcs: UIPageViewController? = pvcw
    if pvcs == nil {
        return
    }
    DispatchQueue.main.async(execute: {() -> Void in
        pvcs?.setViewControllers(viewControllers, direction: direction, animated: false) {(_ finished: Bool) -> Void in }
    })
})

Any ideas? Thank you.

Update

I noticed that sometimes the contained View Controller can be off centered as opposed to entirely missing.

View Controller Off Centered

I looked deeper into the scenario of the view controller missing entirely. Clicking on "Debug View Hierarchy" and turning on "Show Clipped Content" revealed the following when the View Controller is missing entirely:

Turning on Show Clipped Content

Clipped Content

So, it seems the missing content is clipped / out of bounds.

Showing only the wireframes reveals the following:

wire frames only

The Page View Controller has a

  • _UIPageViewControllerContentView that contains a
  • _UIQueuingScrollView that contains a
  • UIView that contains a
  • VehicleDetailTableViewController (the UITableViewController with a car image and details).

I also see the _UIQueuingScrollView's bounds is quite different when things are weird. The bounds have an x of 1125 as opposed to an X of 375 when everything is normal.

This only happens when using a Transition Style of scroll as opposed to Page Curl. When using Page Curl, things work fine.

How can we prevent / fix this?

Second Update

This code makes the problem go away. However, it leaves a more jarring experience. Perhaps due to the delay of 0.4 seconds, the blue background shows sometimes in normal use.

private func show(viewController:UIViewController, going direction: UIPageViewControllerNavigationDirection) {

    let viewControllers = [viewController]
    setViewControllers(viewControllers, direction: direction, animated: true, completion: { (_) in

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.4, execute: {
            self.setViewControllers(viewControllers, direction: direction, animated: false, completion: nil)
        })
    })
}

This is not a good user experience. Is there a better approach?

I want the scroll transitions to be smooth, not briefly show the blue background, and to not lose its content aka View Controller content.


Solution

  • Although the real answer is to have View Controllers that are as simple as possible (but no simpler), here is the code that fixed the problem with the side effect of showing the background on occasion when the user navigates to the next View Controller.

    private func show(viewController:UIViewController, going direction: UIPageViewControllerNavigationDirection) {
    
        let viewControllers = [viewController]
        setViewControllers(viewControllers, direction: direction, animated: true, completion: { (_) in
    
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.4, execute: {
                self.setViewControllers(viewControllers, direction: direction, animated: false, completion: nil)
            })
        })
    }