如何解决“无法确定滚动的导航方向”错误
Posted
技术标签:
【中文标题】如何解决“无法确定滚动的导航方向”错误【英文标题】:How to solve "Failed to determine navigation direction for scroll" bug 【发布时间】:2013-11-12 20:16:17 【问题描述】:我最常见的错误是“无法确定滚动的导航方向”,有什么想法可以解决吗?
这是最后一个异常回溯:
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)
更新: 我终于设法在模拟器上重现了这个错误,当我触摸一个视图时,同时 UIPageViewController 滚动动画以编程方式启动。基本上,如果您以编程方式 setViewsControllers 将动画设置为 yes 并滚动动画。如果在滚动动画开始之前触摸屏幕的任何部分,将会出现以下崩溃 *** Assertion failure in -[_UIQueuingScrollView _didScrollWithAnimation:force:], /SourceCache/UIKit/UIKit-2372/_UIQueuingScrollView.m:778如here 所述。
我还下载了 Apple 的 PhotoScroller 示例应用程序,并通过程序化页面更改对其进行了编辑,它们也存在相同的错误。
我的解决方案是如果用户当前正在触摸屏幕,则不触发页面更改,您也可以将动画更改为卷曲或删除动画。
【问题讨论】:
需要更多信息。这发生在什么时候?您的视图层次结构是什么?在不提供更多上下文的情况下,转储堆栈跟踪不会让任何人都清楚正在发生的事情。 好吧,首先崩溃只发生在 ios7 中。 @CaptainRedmuff 我从来没有在我的模拟器上看到过这个错误(我还在用 Xcode 4 编译,所以我看不到 iOS7 的错误)。我正在使用 UIPageViewController,它经常使用滚动,但我没有更多信息 我可以在 iOS 7 中通过选择我的下一个按钮(它以编程方式移动 viewpager)并在松开之前向左或向右拖动来重新创建它。您有时还以编程方式分页吗? @KyleClegg,没能做到。您按下按钮,开始拖动,然后在拖动时松开按钮自动翻页? 【参考方案1】:理想情况下,我们想知道底层UIScrollView
是被拖拽还是减速。但是 Apple 不允许公共访问页面控制器中的 UIScrollView
,因此我们可以通过这两个委托使用 BOOL 来跟踪页面控制器的转换状态。
- (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;
在以编程方式转换页面视图的代码中,只有在页面控制器尚未转换时才继续。
if (!self.isTransitioning)
[self.pageController setViewControllers:@[toViewController]
direction:UIPageViewControllerNavigationDirectionForward
animated:YES
completion:nil];
【讨论】:
如果您以编程方式更改视图控制器,您将不会收到这些事件,所以这并不能解决它 对我来说就像一个魅力。【参考方案2】:正如我在问题更新中提到的,UIPageViewController 似乎在更改页面动画结束时在以下情况下崩溃:
以编程方式更改页面 页面变化是动画的 TransitionStyle 等于 UIPageViewControllerTransitionStyleScroll 页面更改开始时用户正在触摸屏幕我能够重现 Apple 的 PhotoScroller 示例应用程序中的错误,只需添加一个程序化页面更改。所以这个bug似乎是苹果的错。
由于我无法解决错误,我删除了触发错误的条件之一(等待更好的解决方案)。我不同意使用卷曲过渡(UIPageViewControllerTransitionStyleCurl)或删除过渡动画。这两种解决方案都有效。因此,当用户触摸屏幕时,我不会通过以下行触发程序化页面更改
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];
currentViewController.view 是 UIView 的子类,在 header 中实现以下属性
@property (nonatomic, assign) BOOL isBeingTouch;
以及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;
我只有按钮作为子视图,所以它适用于大多数情况。但是,可能有更完整的解决方案
【讨论】:
【参考方案3】:一个相当老套的解决方案,但这让我可以防止崩溃。
第 1 步:收集错误的完整痕迹
我使用了 Firebase Crashlytics,它让我能够查明问题。
就我而言:
Fatal Exception: NSInternalInconsistencyException
Failed to determine navigation direction for scroll
在 UIScrollview 内部触发崩溃的行是:
scrollView.setContentOffset(CGPoint(x: screenWidth, y: 0), animated: false)
当我在页面之间执行快速滚动时发生这种情况(到达每个页面时,我正在改变滚动行为)。
第 2 步:寻找替代品
由于函数不会抛出,我不得不为我的逻辑找到一个替代品,就我而言:
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)
我只是将 setContentOffset(_:) 调用替换为以下内容(在我的情况下非常有效):
scrollView.isScrollEnabled = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2)
scrollView.isScrollEnabled = true
【讨论】:
以上是关于如何解决“无法确定滚动的导航方向”错误的主要内容,如果未能解决你的问题,请参考以下文章