28

My most frequent bug has "Failed to determine navigation direction for scroll" for reason, any idea about how I could solve it?

Here is the last Exception Backtrace:

 1. CoreFoundation   __exceptionPreprocess + 131
 2. libobjc.A.dylib  _objc_exception_throw + 39
 3. CoreFoundation   +[NSException raise:format:] + 1
 4. Foundation   -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 91
 5. UIKit    __54-[_UIQueuingScrollView _didScrollWithAnimation:force:]_block_invoke + 221
 6. UIKit    -[_UIQueuingScrollView _didScrollWithAnimation:force:] + 567
 7. UIKit    -[_UIQueuingScrollView _scrollViewAnimationEnded:finished:] + 73
 8. UIKit    -[UIAnimator stopAnimation:] + 471
 9. UIKit    -[UIAnimator(Static) _advanceAnimationsOfType:withTimestamp:] + 285
 10. UIKit   -[UIAnimator(Static) _LCDHeartbeatCallback:] + 53
 11. QuartzCore  CA::Display::DisplayLinkItem::dispatch() + 99
 12. QuartzCore  CA::Display::DisplayLink::dispatch_items(unsigned long long, unsigned long long, unsigned long long) + 345
 13. IOMobileFramebuffer     IOMobileFramebufferVsyncNotifyFunc + 105
 14. IOKit   _IODispatchCalloutFromCFMessage + 249
 15. CoreFoundation  __CFMachPortPerform + 137
 16. CoreFoundation  __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 35
 17. CoreFoundation  __CFRunLoopDoSource1 + 347
 18. CoreFoundation  __CFRunLoopRun + 1399
 19. CoreFoundation  _CFRunLoopRunSpecific + 523
 20. CoreFoundation  _CFRunLoopRunInMode + 107
 21. GraphicsServices    _GSEventRunModal + 139
 22. UIKit   _UIApplicationMain + 1137
 23. MyApp   main (main.m:13)

UPDATE : I finally managed to reproduce the bug on the simulator, it's when I'am touching a view and that at the same time, the UIPageViewController scroll animation starts programmatically. Basically, if you setViewsControllers programmatically with animation set to yes and scroll animation. If you're touching any part of the screen before the scroll animation starts there will be the following crash *** Assertion failure in -[_UIQueuingScrollView _didScrollWithAnimation:force:], /SourceCache/UIKit/UIKit-2372/_UIQueuingScrollView.m:778 as described here .

I also downloaded Apple's PhotoScroller sample app and edited it with programmatic page change and they have the same bug.

My solution was not to trigger the page change if the user is currently touching the screen, you can also change the animation to curl or remove the animation.

Community
  • 1
  • 1
Matthieu Rouif
  • 2,843
  • 1
  • 19
  • 28
  • 1
    More information is required. When does this occur? What is your view hierarchy? Dumping a stack trace isn't going to make it obvious to anyone what is happening without providing more context. – Zack Brown Nov 26 '13 at 11:04
  • Well, first the crash only happens in iOS7. – Matthieu Rouif Nov 26 '13 at 18:49
  • @CaptainRedmuff I never saw this bug on my simulator (I was still compiling with Xcode 4, so I couldn't see iOS7 bugs). I am using UIPageViewController which use scrolling a lot but I don't have much more information – Matthieu Rouif Nov 26 '13 at 19:14
  • I'm able to recreate this in iOS 7 by selecting my next button (which moves the viewpager programmatically) and dragging left or right before letting go. Are you also paging programmatically at times? – Kyle Clegg Dec 08 '13 at 16:58
  • @KyleClegg, didn't manage to do so. You push down the button, start dragging, and let go the button to page automatically while still dragging? – Matthieu Rouif Dec 13 '13 at 10:47
  • Not sure why this is closed. Part the problem is that it's so incredibly hard to reproduce. https://twitter.com/dcondrau/status/284297654899453954 http://software.techassistbox.com/how-to-solve-failed-to-determine-navigation-direction-for-scroll-bug-on-hold_57743.html @MatthieuRouif any luck? – Ryan Romanchuk Jan 13 '14 at 21:47
  • @RyanRomanchuk I am trying to push the app in production with XCode 5, as it's a problem only with iOS7, my guess is that XCode5 might solve the bug. No other ideas so far – Matthieu Rouif Jan 14 '14 at 11:43
  • @kyle-clegg I actually isolated that this bug is only happening when calling setViewControllers:direction:animated:completed with animated set to YES. – Ryan Romanchuk Jan 14 '14 at 17:55
  • This issue still exists in iOS 7.1, but seems to be resolved in iOS 8.0 – lehn0058 Oct 20 '14 at 13:35
  • Actually the bug is happening for me right now is iOS 8 :(. I know I'm late, but are there any solutions that any of you have used to fix this yet? – Rafi Aug 13 '15 at 22:05
  • This is the question I asked: http://stackoverflow.com/questions/31977977/ios-uipageviewcontroller-failed-to-determine-navigation-direction?noredirect=1#comment51888367_31977977 – Rafi Aug 13 '15 at 22:06

3 Answers3

8

Ideally we would want to know whether the underlying UIScrollView is being dragged or decelerating. But Apple doesn't allow public access to the UIScrollView in the page controller, so we can keep track of the page controller's transition state with a BOOL via these two delegates.

- (void)pageViewController:(UIPageViewController *)pageViewController willTransitionToViewControllers:(NSArray *)pendingViewControllers
{
    self.transitioning = YES;
}

- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed
{
    if (finished) {
        self.transitioning = NO;
    }
}

In your code to programmatically transition the page views, only proceed if the page controller isn't already transitioning.

if (!self.isTransitioning) {
    [self.pageController setViewControllers:@[toViewController]
                                  direction:UIPageViewControllerNavigationDirectionForward
                                   animated:YES
                                 completion:nil];
}
James Kuang
  • 10,710
  • 4
  • 28
  • 38
4

As I mention in the question update, UIPageViewController seems to crash in the following conditions at the end of the change page animation:

  • Change page programmatically
  • Page change is animated
  • TransitionStyle is equal to UIPageViewControllerTransitionStyleScroll
  • User is touching the screen when the page change starts

I was able to reproduce the bug in Apple's PhotoScroller sample app, just by adding a programmatic page change. So the bug seems to be Apple's fault.

As I wasn't able to solve the bug, I remove one of the condition that trigger the bug (waiting for a better solution). I wasn't ok with using a curl transition (UIPageViewControllerTransitionStyleCurl) or removing the transition animation. Both these solutions work. So, when user is touching the screen I don't trigger the programmatic page change with the following line

UIViewController* currentViewController = [self.viewControllers objectAtIndex:0];
if(currentViewController.view.isBeingTouch){
        ContentViewController *nextViewController = [[ContentViewController alloc] init];
        NSArray *viewControllers =;
        [self setViewControllers: @[loadingController] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
}

With currentViewController.view being a subclass of UIView implementing the following property in header

@property (nonatomic, assign) BOOL isBeingTouch;

and the following methods in main

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    _isBeingTouch = YES;
    [super touchesBegan:touches withEvent:event];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
    _isBeingTouch = NO;
    [super touchesCancelled:touches withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    _isBeingTouch = NO;
    [super touchesEnded:touches withEvent:event];
}

-(BOOL)isBeingTouch{

    for (UIView * view in self.subviews){
        if([view isKindOfClass:[UIButton class]]) {
            UIButton * b = (UIButton *) view;
            if(b.isHighlighted)
                return YES;
        }
    }

    return _isBeingTouch;
}

I just have buttons as subviews, so it works for most of the cases. However, there might a more complete solution

Matthieu Rouif
  • 2,843
  • 1
  • 19
  • 28
0

A quite hacky solution but this allowed me to prevent the crash.

Step 1: Gather a full trace of the bug

I used Firebase Crashlytics which allowed me to pinpoint the issue.

In my case:

Fatal Exception: NSInternalInconsistencyException
Failed to determine navigation direction for scroll

The line triggering the crash in UIScrollview internals was:

scrollView.setContentOffset(CGPoint(x: screenWidth, y: 0), animated: false)

This occured when I performed fast scrolls between pages (when reaching each page, I was altering the scroll behaviour).

Step 2: Finding a replacement

As the function does not throw, I had to find a replacement for my logic, in my case:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
        //print("contentOffset = \(scrollView.contentOffset.x)")

        if !self.isLeftScrollEnabled {
            disableLeftScroll(scrollView)
        }
        //...
}

func disableLeftScroll(_ scrollView: UIScrollView) {
    let screenWidth = UIScreen.main.bounds.width
        if scrollView.contentOffset.x < screenWidth {
         // ERROR
         scrollView.setContentOffset(CGPoint(x: screenWidth, y: 0), animated: false)
     }

}

I simply replaced the setContentOffset(_:) call with the following (which works perfectly in my case):

scrollView.isScrollEnabled = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
    scrollView.isScrollEnabled = true
} 
michael-martinez
  • 767
  • 6
  • 24