xcode 在 ios6 中,将 pageViewController 的gestureRecognizers 委托设置为viewController 会导致崩溃
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12561336/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
In ios6, setting your pageViewController's gestureRecognizers delegate to a viewController causes a crash
提问by noRema
This has only started happening with ios6, but if you start a new project using the page view controller template. Then in
这仅在 ios6 中开始发生,但是如果您使用页面视图控制器模板启动一个新项目。然后在
PCRootViewControlle::viewDidLoad()
add the lines to the bottom of the method.
将这些行添加到方法的底部。
for (UIGestureRecognizer *gR in self.pageViewController.gestureRecognizers)
{
gR.delegate = self;
}
You'll need to assign the viewController so it conforms to the UIGestureRecognizerDelegate and implement the method
您需要分配 viewController 使其符合 UIGestureRecognizerDelegate 并实现该方法
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch (UITouch *)touch
{
return YES;
}
Now if you run the app and try to turn the page beyond the bounds, i.e. go to January and try to turn back so
现在,如果您运行该应用程序并尝试将页面翻过边界,即转到一月并尝试返回
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
returns nil.
返回零。
The app will then crash.
然后应用程序将崩溃。
This did not happen with ios5. I need to assign the gestureRecognizer delegate to my viewController because I do not always want the pageViewController to handle the touch events.
ios5 没有发生这种情况。我需要将gestureRecognizer 委托分配给我的viewController,因为我并不总是希望pageViewController 处理触摸事件。
Has any else experienced this or point out If I am doing something wrong?
有没有其他人经历过这个或指出我做错了什么?
Many Thanks Stewart.
非常感谢斯图尔特。
采纳答案by noRema
Finally found a solution to my problem which has caused me grief so hopefully this can help someone else.
终于找到了解决我的问题的方法,这让我感到悲伤,所以希望这可以帮助其他人。
The issue is if you set the pageViewControllers delegate to your viewController
问题是如果您将 pageViewControllers 委托设置为您的 viewController
for (UIGestureRecognizer *gR in self.pageController.view.gestureRecognizers)
{
if ([gR isKindOfClass:[UITapGestureRecognizer class]])
{
gR.enabled = NO;
}
else if ([gR isKindOfClass:[UIPanGestureRecognizer class]])
{
gR.delegate = self;
}
}
then returning nil from
然后从
pageViewController:viewControllerAfterViewController:
will crash!! only in iOS6!!
会崩溃!!仅在 iOS6 中!!
My issue was that I needed to set the delegate of the gestureRecognisers because I needed to intercept the panGesture in some situations, i.e not allowing the user to turn a page wilst touching certain parts of it due to some buttons there.
我的问题是我需要设置gestureRecognisers的委托,因为我需要在某些情况下拦截panGesture,即由于那里的某些按钮,不允许用户在触摸它的某些部分时翻页。
The solution is to put the logic from
解决方案是将逻辑从
pageViewController:viewControllerAfterViewController:
into
进入
gestureRecognizer:shouldReceiveTouch:
because as long as we return NO from that then it wont go on to call
因为只要我们从中返回 NO 那么它就不会继续调用
pageViewController:viewControllerAfterViewController:
and so no need to return nil and get a crash.
因此无需返回 nil 并导致崩溃。
However, this didn't work on the first and last page in the sequence. For example on the first page, you want to allow the page to turn forward but not back. So I thought about looking at the GestureRecogniser passed in, casting it to a PanGesture, and then checking the velocity on this, if the velocity signifies turning back ( > 0.0f ) then just return NO. This sounded good but the velocity was always zero.
但是,这不适用于序列中的第一页和最后一页。例如,在第一页上,您希望允许页面向前翻但不向后翻。所以我考虑查看传入的 GestureRecogniser,将其转换为 PanGesture,然后检查速度,如果速度表示返回(> 0.0f),则返回 NO。这听起来不错,但速度始终为零。
I then found a very helpful little function on the GestureRecognizer delegate called:
然后我在 GestureRecognizer 委托上发现了一个非常有用的小函数,称为:
gestureRecognizerShouldBegin:gestureRecognizer
this function is called after
此函数在调用之后
gestureRecognizer:shouldReceiveTouch:
but this time the velocity from the gesture is as I expected, so I can check the velocity and only return YES for the first page if it is > 0.0f
但这次手势的速度和我预期的一样,所以我可以检查速度,如果第一页的速度 > 0.0f,则只返回 YES
回答by jankins
I believe I've found a solution that's much less convoluted than refactoring any existing pageViewController code, as noRema mentioned, or messing with selector forwarding, as mentioned here, though I would never have found it without both of those solutions as reference.
我相信我已经发现了在更令人费解比重构现有pageViewController代码,noRema提及,或选择转发搞乱,如提到的解决方案在这里,但我永远不会发现它没有这两个解决方案作为参考。
This should easily drop into any UIPageViewController project with only minor adjustments.
这应该很容易放入任何 UIPageViewController 项目,只需稍作调整。
To implement, you leave your code as-is; all existing logic should remain the same as this only affects UIGestureRecognizer Delegate behavior. Override the gesture delegate method - (BOOL)gestureRecognizerShouldBegin: with something like the following:
要实现,您将代码保持原样;所有现有逻辑都应保持不变,因为这只会影响 UIGestureRecognizer 委托行为。覆盖手势委托方法 - (BOOL)gestureRecognizerShouldBegin: 类似于以下内容:
NOTE: I'm using Apple's Page-Based Application code as a starting point, so refer to that if the terms _modelController
and _pageViewController
cause any confusion.
注:我使用的是苹果公司基于网页的应用程序代码为出发点,因此是指,如果条款_modelController
和_pageViewController
造成任何混乱。
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
//Make sure we're not trying to turn backward past the first page:
if ([_modelController indexOfViewController:[_pageViewController.viewControllers objectAtIndex:0]] == 0) {
if ([(UIPanGestureRecognizer*)gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] &&
[(UIPanGestureRecognizer*)gestureRecognizer velocityInView:gestureRecognizer.view].x > 0.0f) {
NSLog(@"DENIED SWIPE PREVIOUS ON FIRST PAGE");
return NO;
}
if ([(UITapGestureRecognizer*)gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] &&
[(UITapGestureRecognizer*)gestureRecognizer locationInView:gestureRecognizer.view].x < self.view.frame.size.width/2) {
NSLog(@"DENIED TAP PREVIOUS ON FIRST PAGE");
return NO;
}
}
//Make sure we're not trying to turn forward past the last page:
int finalVCindexSubtractor;
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) {
// the vc we compare is a different distance from the end based on our orientation:
finalVCindexSubtractor = 2;
} else {
finalVCindexSubtractor = 1;
}
if ([_modelController indexOfViewController:[_pageViewController.viewControllers objectAtIndex:0]] == _modelController.pageData.count-finalVCindexSubtractor) {
if ([(UIPanGestureRecognizer*)gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] &&
[(UIPanGestureRecognizer*)gestureRecognizer velocityInView:gestureRecognizer.view].x < 0.0f) {
NSLog(@"DENIED SWIPE NEXT ON LAST PAGE");
return NO;
}
if ([(UITapGestureRecognizer*)gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] &&
[(UITapGestureRecognizer*)gestureRecognizer locationInView:gestureRecognizer.view].x > self.view.frame.size.width/2) {
NSLog(@"DENIED TAP NEXT ON LAST PAGE");
return NO;
}
}
return YES;
}
回答by Sergii N.
While investigating issue with Pan Gesture in UIPageViewController under iOS 6 I have noticed that it could invoke before/after view controller methods several times within one gesture. That is bad in my particular case, as I need to wait until animation is finished for each page turn to be able to navigate to next/previous page. Thus our methods have such a structure:
在调查 iOS 6 下 UIPageViewController 中 Pan Gesture 的问题时,我注意到它可以在一个手势内多次调用 before/after 视图控制器方法。这在我的特定情况下很糟糕,因为我需要等到动画完成每个页面才能导航到下一页/上一页。因此我们的方法具有这样的结构:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController {
if (animationInProgress)
return nil;
UIViewController *result = nil;
//do stuff to init result controller for next/prev page
if (result)
animationInProgress = YES;
return result;
}
Same for viewControllerAfterViewController
method.
viewControllerAfterViewController
方法相同。
And after animation finishes, this method of UIPageViewControllerDelegate is invoked:
动画结束后,调用 UIPageViewControllerDelegate 的这个方法:
- (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
animationInProgress = NO;
//do other stuff to handle finishing animation.
}
As PanGesture was invoked several times, then viewControllerAfterViewController
and another method was invoked several times due to that, and unfortunately didFinishAnimating
method was not invoked in this case, which lead to animationInProgress
flag set to YES
always and user was not able to navigate through pages at all. NOTE: happens only in iOS 6.
由于 PanGesture 被多次调用,然后viewControllerAfterViewController
另一个方法因此被调用多次,不幸的didFinishAnimating
是在这种情况下没有调用方法,这导致animationInProgress
标志设置为YES
always 并且用户根本无法浏览页面。注意:仅在 iOS 6 中发生。
To fix that issue we had to handle PanGesture somehow. Using UIView gestureRecognizerShouldBegin:gestureRecognizer
(which was added in iOS 6) did not solve an issue at all, setting controller as delegate for gesture lead to crashes when nil is returned from before/after methods. Thus we tried to add logics to not invoke before/after controller methods in cases when PanGesture is in UIGestureRecognizerStateChanged
state (that was investigated based on some testing to determine what is gesture state when it invokes methods in loop).
为了解决这个问题,我们必须以某种方式处理 PanGesture。使用 UIView gestureRecognizerShouldBegin:gestureRecognizer
(在 iOS 6 中添加)根本没有解决问题,当从 before/after 方法返回 nil 时,将控制器设置为手势的委托会导致崩溃。因此,我们尝试添加逻辑,以便在 PanGesture 处于UIGestureRecognizerStateChanged
状态时不调用控制器方法之前/之后(根据一些测试进行调查,以确定当它在循环中调用方法时确定什么是手势状态)。
By adding method like:
通过添加方法,如:
- (BOOL)skipIfPanGestureInProgress {
BOOL shouldSkip = NO;
if(pvcPanGestureRecognizer && [pvcPanGestureRecognizer state] == UIGestureRecognizerStateChanged){
animationInProgress = NO;
shouldSkip = YES;
}
return shouldSkip;
}
After that methods look like:
之后的方法看起来像:
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController {
if([self skipIfPanGestureInProgress]){
return nil;
}
if (animationInProgress)
return nil;
UIViewController *result = nil;
//do stuff to init result controller for next/prev page
if (result)
animationInProgress = YES;
return result;
}
This helped to solve loop problem with animationInProgress flag.
这有助于解决 animationInProgress 标志的循环问题。
Hope this will be helpful for someone.
希望这对某人有帮助。
回答by Liam
I had the same issue with UIPageViewController crashing with iOS6 with the same error when navigating beyond the bounds.
我在 UIPageViewController 与 iOS6 一起崩溃时遇到了同样的问题,当导航超出边界时出现相同的错误。
None of the above solutions worked for me but I eventually found that moving the following line from viewDidLoad
to viewDidAppear
fixed it completely.
上述解决方案均不适合我,但我最终发现将以下行从 移动viewDidLoad
到viewDidAppear
完全修复。
self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;