1

It appears that there is an issue in UIPageViewController that I cannot get around the Assertion error below

Assertion failure in -[XXX.UIPagingViewController queuingScrollView:didEndManualScroll:toRevealView:direction:animated:didFinish:didComplete:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit/UIKit-3698.33.3.301/UIPageViewController.m:2067

I have looked through the following answers, and many more, but I have not being able to isolate the answer, but believe I have a different question in isolating the issue.

Assertion failure in UIPageViewController #1

Assertion failure in UIPageViewController #2

UIPageViewController didFinishAnimating not called if swiped quickly

After doing some research, I figured I'd ask a more specific question relating to the problem at hand based on my findings in the Hacked Solution below

Implementation

I implemented the UIPageViewController to display 3 view controllers vertically in full screen. Here is my implementation, followed by my observations, and a hacked up solution that I cannot fully understand.

UIPageViewControllerDataSource

func pageViewController(_ pageViewController: UIPageViewController,
                        viewControllerBefore viewController: UIViewController) -> UIViewController? 
{
    if let viewController = viewController as? HomeItemPageViewController,
        let index = viewControllers.index(of: viewController) 
    {
            if index == 0 { return nil }
            return viewControllerAtIndex(index: max(0, index - 1))
    }

    return nil
}

func pageViewController(_ pageViewController: UIPageViewController,
                        viewControllerAfter viewController: UIViewController) -> UIViewController? 
{
    if viewControllers.count > 0 
    {
        if let vc = viewController as? HomeItemPageViewController,
           let index = viewControllers.index(of: vc), index + 1 < viewControllers.count
        {
            return viewControllerAtIndex(index: index + 1)
        }
    }

    return nil
}

UIPageViewControllerDataSource

public func pageViewController(_ pageViewController: UIPageViewController, willTransitionTo pendingViewControllers: [UIViewController]) 
{
    print("[PageViewController] - willTransitionTo - pendingViewControllers \(pendingViewControllers)")
}

public func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) 
{
    print("[PageViewController] - didFinishAnimating - finished \(finished) - completed \(completed) ")
}

Scenario 1

I flicked from controller to controller, and in each case the willTransitionTo and didFinishAnimating got triggered accordingly as they should.

Scenario 2

After scrolling from the first page (index 0) to the middle one (index 1), I can clearly see a call to willTransitionTo, followed by didFinishAnimating.

Now if I start panning to the next index 3, but release it before the animation to index 3 occurs, only willTransitionTo is getting called. The next step was to begin panning to the first page at index 0, which then triggers the assertion error ....

Hacked Solution (Please help here)

By swizzling the method in the Assertion failue as follows following, I tried to figure out how it is being triggered to possibly isolate what I could do to to force the completion of transition manually.

final public class func swizzleMethods() 
{
    let original_method = class_getInstanceMethod(FlowPageViewController.self, Selector(("queuingScrollView:didEndManualScroll:toRevealView:direction:animated:didFinish:didComplete:")))
    let swizzled_method = class_getInstanceMethod(FlowPageViewController.self, Selector(("_queuingScrollView:didEndManualScroll:toRevealView:direction:animated:didFinish:didComplete:")))

    method_exchangeImplementations(original_method!, swizzled_method!)
}

@objc func _queuingScrollView(_ arg1: Any, didEndManualScroll arg2: Bool, toRevealView arg3: Any, direction arg4: Int, animated arg5: Bool, didFinish arg6: Bool, didComplete arg7: Bool) 
{
      print("[FlowPageViewController] - Swizzle Ignore")
}

I was not able to fix it, or able to figure out exactly what I could do to get the delegate to perform the didFinishAnimating call as it should. But the odd observation that came out of it, that once the method is swizzled, and I don't call the original one, everything seems to work as it should without any consequences.

I believe that since the method is private it will not get approved by able, so I'm wondering if anyone know how I could possibly force the UIPageViewController to complete it's transition, or how I can cancel the transition relative to the centered view displayed in the backing ScrollView?

Any insight into this would be highly appreciated.

AntonTheDev
  • 899
  • 6
  • 13
  • facing same issue , are you able to find the solution? – Dhiru May 28 '19 at 05:23
  • @Dhiru If I recall, I made second paging controller and set the alpha epsilon, which basically allows it to reload tricking the system into thinking it visible, while it actually is near invisible, and scrolled to the correct page. Then basically performed some animations animated between the two, and removed the old one. I was never able to make it work correctly – AntonTheDev May 28 '19 at 07:49

0 Answers0