【问题标题】:In ios6, setting your pageViewController's gestureRecognizers delegate to a viewController causes a crash在 ios6 中,将 pageViewController 的 gestureRecognizers 委托设置为 viewController 会导致崩溃
【发布时间】:2012-09-24 08:27:47
【问题描述】:

这仅在 ios6 上开始发生,但如果您使用页面视图控制器模板开始一个新项目。然后在

PCRootViewControlle::viewDidLoad()

将行添加到方法的底部。

for (UIGestureRecognizer *gR in self.pageViewController.gestureRecognizers)
{
    gR.delegate = self;
}

您需要分配 viewController 使其符合 UIGestureRecognizerDelegate 并实现该方法

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch (UITouch *)touch
{
    return YES;
}

现在,如果您运行应用程序并尝试将页面翻到边界之外,即转到 1 月并尝试返回,那么

  • (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController

返回零。

然后应用程序将崩溃。

这在 ios5 中没有发生。我需要将gestureRecognizer 委托分配给我的viewController,因为我并不总是希望pageViewController 处理触摸事件。

有没有其他人经历过或指出我做错了什么?

非常感谢 斯图尔特。

【问题讨论】:

  • 错误日志...由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“提供的视图控制器的数量 (0) 与请求的转换所需的数量 (2) 不匹配'
  • 我遇到了同样的问题,在这里写下了我的解决方案。 stackoverflow.com/questions/12565400/…
  • 我在这里发布了同样问题的答案:stackoverflow.com/a/12573631/1655630
  • 在下面查看我的答案,它避免了同一页的无限翻页

标签: objective-c ios xcode ios6


【解决方案1】:

终于找到了解决我的问题的方法,这让我很伤心,所以希望这可以帮助其他人。

问题是如果您将 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;
    }
}

然后从

返回 nil
pageViewController:viewControllerAfterViewController:

会崩溃!!仅限iOS6!!

我的问题是我需要设置gestureRecognisers的代表,因为我需要在某些情况下拦截panGesture,即由于那里的一些按钮,不允许用户在触摸页面的某些部分时翻页。

解决办法是把逻辑从

pageViewController:viewControllerAfterViewController: 

进入

gestureRecognizer:shouldReceiveTouch:

因为只要我们返回 NO 就不会继续调用

pageViewController:viewControllerAfterViewController:

因此无需返回 nil 并导致崩溃。

但是,这不适用于序列中的第一页和最后一页。例如,在第一页上,您希望允许页面向前而不是向后。所以我考虑查看传入的 GestureRecogniser,将其转换为 PanGesture,然后检查其速度,如果速度表示返回(> 0.0f),则返回 NO。这听起来不错,但速度始终为零。

然后我在 GestureRecognizer 委托上发现了一个非常有用的小功能:

gestureRecognizerShouldBegin:gestureRecognizer

这个函数在

之后被调用
gestureRecognizer:shouldReceiveTouch:

但是这次手势的速度与我预期的一样,所以我可以检查速度,如果第一页 > 0.0f 则只返回 YES

【讨论】:

    【解决方案2】:

    我相信我找到了一个解决方案,它比重构任何现有的 pageViewController 代码(如 noRema 所述)或与选择器转发(如 @ 987654321@ 所述)相比要复杂得多,尽管如果没有这两种解决方案,我永远不会找到它作为参考。

    这应该很容易放入任何 UIPageViewController 项目中,只需稍作调整。

    要实现,您将代码保持原样;所有现有逻辑应保持不变,因为这只影响 UIGestureRecognizer 委托行为。覆盖手势委托方法 - (BOOL)gestureRecognizerShouldBegin: 类似于以下内容:

    注意:我使用 Apple 的基于页面的应用程序代码作为起点,因此如果术语 _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;
    }
    

    【讨论】:

    • @jankins..._modelController 和 _pageViewController 指的是什么?
    • 查看我帖子中的评论:“注意:我使用 Apple 的基于页面的应用程序代码作为起点,因此如果 _modelController 和 _pageViewController 这两个术语引起任何混淆,请参考。”跨度>
    • 速度有时
    • 感谢 效果很好,即插即用!请注意,如果您在横向上禁用了两个页面,我们需要更改 finalVCindexSubtractor = 2;到 1
    【解决方案3】:

    在调查 iOS 6 下 UIPageViewController 中的 Pan Gesture 问题时,我注意到它可以在一个手势内多次调用视图控制器方法之前/之后。在我的特定情况下这很糟糕,因为我需要等到动画完成后才能导航到下一页/上一页。 因此我们的方法具有这样的结构:

    - (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;
    }
    

    viewControllerAfterViewController 方法也一样。

    并且动画结束后,调用UIPageViewControllerDelegate这个方法:

    - (void)pageViewController:(UIPageViewController *)pageViewController didFinishAnimating:(BOOL)finished previousViewControllers:(NSArray *)previousViewControllers transitionCompleted:(BOOL)completed {
            animationInProgress = NO;
            //do other stuff to handle finishing animation.
    }
    

    由于 PanGesture 被多次调用,然后viewControllerAfterViewController 和另一个方法因此被多次调用,不幸的是在这种情况下没有调用didFinishAnimating 方法,导致animationInProgress 标志设置为YES总是,用户根本无法浏览页面。注意:仅在 iOS 6 中发生。

    为了解决这个问题,我们必须以某种方式处理 PanGesture。使用 UIView gestureRecognizerShouldBegin:gestureRecognizer(在 iOS 6 中添加)根本没有解决问题,将控制器设置为手势的委托会导致在从 before/after 方法返回 nil 时崩溃。因此,我们尝试添加逻辑以在 PanGesture 处于UIGestureRecognizerStateChanged 状态时不调用控制器方法之前/之后(根据一些测试进行调查,以确定在循环调用方法时手势状态是什么)。

    通过添加类似的方法:

    - (BOOL)skipIfPanGestureInProgress {
        BOOL shouldSkip = NO;
        if(pvcPanGestureRecognizer && [pvcPanGestureRecognizer state] == UIGestureRecognizerStateChanged){
            animationInProgress = NO;
            shouldSkip = YES;
        }
        return shouldSkip;
    }
    

    之后的方法如下:

    - (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;
    }
    

    这有助于解决 animationInProgress 标志的循环问题。

    希望这对某人有所帮助。

    【讨论】:

      【解决方案4】:

      在导航越界时,我遇到了 UIPageViewController 与 iOS6 崩溃并出现相同错误的相同问题。

      上述解决方案都不适合我,但我最终发现将以下行从 viewDidLoad 移动到 viewDidAppear 完全修复了它。

      self.view.gestureRecognizers = self.pageViewController.gestureRecognizers;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-20
        • 1970-01-01
        • 1970-01-01
        • 2016-12-11
        相关资源
        最近更新 更多