【问题标题】:UIPageViewController returns no Gesture Recognizers in iOS 6UIPageViewController 在 iOS 6 中不返回手势识别器
【发布时间】:2012-10-17 16:53:54
【问题描述】:

我正在尝试禁用 UIPageViewController 的平移手势识别器。

在 iOS 5 上,我可以循环访问它们并禁用它们。

for (UIGestureRecognizer* recognizer in self.pageViewController.gestureRecognizers) {
    if ([recognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
        recognizer.enabled = NO;
    }
}

在使用 UIPageViewControllerTransitionStyleScroll 的 iOS 6 上,页面视图控制器没有返回手势识别器。

澄清

这可以归结为:

当 UIPageViewController 的过渡样式设置为滚动时 self.pageViewController.gestureRecognizers = 0,因此我无法访问手势识别器。

有什么办法可以解决这个问题吗?我认为我没有做错任何事,因为 curl 过渡效果很好。

【问题讨论】:

  • 我看到了完全相同的问题。希望有一个解决方法。
  • 也许 iOS6 没有在 UIPageVC 上使用 UIPanGestureRecognizer。您是否尝试过插入自己的手势识别器并以某种方式强制它覆盖默认的滑动/平移行为?
  • 如果你的过渡风格设置为 UIPageViewControllerTransitionStylePageCurl,这仍然适用于 iOS 6

标签: iphone objective-c uikit uipageviewcontroller


【解决方案1】:

在 UIPageViewController.h 中找到这个:

// 仅当过渡样式为 'UIPageViewControllerTransitionStylePageCurl' 时才填充。 @property(nonatomic, readonly) NSArray *gestureRecognizers;

所以,这不是错误 - 根据设计,pageViewController 在设置滚动样式时不会获得手势识别器。

【讨论】:

  • 好收获。显然应该在标题中查看。我已经将答案授予 Sergio,因为他确实以合理的(尽管是骇人听闻的)解决方法做出了回应。显然,这是一个缺失的功能。
  • @leanne:+1 发现这一点。问题出现了:头文件或参考文档是“真正的”参考吗? Apple 至少可以将该通知添加到其文档中,以免人们头疼……
  • @sergio:考虑到某些语言甚至没有像我们从 Apple 获得的那样提供好的文档,我认为头文件是“真正的”参考。我确实同意,如果此信息包含在参考文档中,事情会变得更容易,因为它已提供(并且非常感谢!)。
  • IMO 这是非常疯狂的行为,我倾向于同意@sergio(尽管@leanne 的任何一种方式都很好)。快速浏览updated 头文件表明,该行为仍然适用于较新版本的 iOS
  • 我认为其中的一个原因可能是 Apple 使用 UIScrollView 滚动 UIPageViewControllers。如果您尝试为 UIScrollView panGestureRecognizer 设置委托,您将收到异常:“NSInvalidArgumentException”,原因:“UIScrollView 的内置平移手势识别器必须将其滚动视图作为其委托。”我猜他们出于这个原因选择不公开手势识别器。
【解决方案2】:

您总是可以尝试在页面视图控制器的子视图上禁用用户交互:

for (UIScrollView *view in self.pageViewController.view.subviews) {
    if ([view isKindOfClass:[UIScrollView class]]) {
        view.scrollEnabled = NO;
    }
}

【讨论】:

  • 这是一个更简单的问题解决方案
  • 几乎看过这个,因为支持的票太少了。这非常有效。
  • 这个比sergio的解决方案简单干净的解决方案
  • 这是当 PVC-transitionStyle = Scroll 时的 解决方案。
  • 这依赖于系统类的内部视图层次结构。此类解决方案本质上是脆弱的,这可能会在操作系统的任何甚至很小的更新中停止工作。
【解决方案3】:

此行为有一个bug filed in radar。所以,我敢打赌,在 Apple 修复它之前,没有机会解决这个问题。

我想到的一种解决方法是在您的 UIPageViewController 上放置一个透明的子视图,并添加一个 UIPanGestureRecognizer 以拦截这种手势而不是进一步前进。您可以在需要禁用手势时启用此视图/识别器。

我尝试了 Pan 和 Tap 手势识别器的组合,它可以工作。

这是我的测试代码:

- (void)viewDidLoad {
  [super viewDidLoad];

   UIPanGestureRecognizer* g1 = [[[UIPanGestureRecognizer alloc] initWithTarget:self
                                                                      action:@selector(g1Pan:)] autorelease];
  [self.view addGestureRecognizer:g1];

  UITapGestureRecognizer* s1 = [[[UITapGestureRecognizer alloc] initWithTarget:self
                                                                      action:@selector(g1Tap:)] autorelease];

  [self.view addGestureRecognizer:s1];

  UIView* anotherView = [[[UIView alloc]initWithFrame:self.view.bounds] autorelease];
  [self.view addSubview:anotherView];

  UIPanGestureRecognizer* g2 = [[[UIPanGestureRecognizer alloc] initWithTarget:self
                                                                      action:@selector(g2Pan:)] autorelease];
  [anotherView addGestureRecognizer:g2];

}

启用g2 时,会阻止g1 被识别。另一方面,它不会阻止 s1 被识别。

我知道这是 hack,但面对 UIPageViewController 中的一个看似错误(至少,实际行为与参考状态明显不同),我找不到更好的解决方案。

【讨论】:

  • 看起来leanne 是对的,手势识别器只为UIPageViewControllerTransitionStylePageCurl 提供。所以这更像是一个功能请求而不是一个错误。在此期间,我可能会使用与您建议的类似的解决方法。
  • 您可以通过在当前建议的页面上添加手势识别器来获得或多或少相同的效果。使用平移手势识别器吃掉平移,页面视图控制器永远不会得到它们。简单一点。
  • 奇怪,这适用于我所有测试用例的 95%。但有时潜在的手势无论如何都会通过。也许是一个错误,而且是随机的。
  • 令人沮丧的是,除了头文件之外,有些东西可以记录为在任何地方都有一种行为
【解决方案4】:

根据UIPageViewController头文件,当datasource为nil时,手势导航是禁用的。

所以,当你想禁用滑动时,将 datasource 设置为 nil,然后当你想要启用滑动时,重置数据源。

// turns off paging
pageViewController.datasource = nil

// turns on paging
pageViewController.datasource = self;

【讨论】:

  • 这绝对是最优雅的解决方案,使用 Apple 官方 API 文档中声明的行为最少并利用它。
  • 这在我的情况下不起作用。我在屏幕上有一个文本字段。显示键盘时,我想锁定屏幕。然而,将 PVC 设置为 nil 似乎也会禁用 [textfield resignFirstResponder];因此,我选择了 dStolic 关于设置每个 PVC 的 (scrollview)subview.scrollEnabled = NO; 的回答
【解决方案5】:

您可以通过 UIPageViewController 中的 UIScrollview 访问 UIPanGestureRecognizer。

for (UIView *view in self.pageController.view.subviews) {
    if ([view isKindOfClass:[UIScrollView class]])
    {
        UIScrollView *scrollView = (UIScrollView *)view;

        UIPanGestureRecognizer* panGestureRecognizer = scrollView.panGestureRecognizer;
        [panGestureRecognizer addTarget:self action:@selector(move:)];
    }
}

【讨论】:

    【解决方案6】:

    如果 UIPageViewControllers UIPanGestureRecognizer 正在吃掉/吞下其他 PanGestureRecognizer 的所有事件(例如滑动菜单)。

    您可以轻松扩展 Bernies 解决方案,并使 UIScrollViews PanGestureRecognizer 需要其他识别器失败。 像这样的:

    for (UIView *view in pageViewController.view.subviews) {
        if ([view isKindOfClass:[UIScrollView class]]){
            UIScrollView *scrollView = (UIScrollView *)view;
    
            [scrollView.panGestureRecognizer requireGestureRecognizerToFail:otherRecognizer];
        }
    }
    

    这样滚动的 PanGestureRecognizer 只会在我打算让他去的区域触发。

    这可能不是最好的未来证明解决方案,因为我们要求 Apple 不要更改 UIPageViewControllers 内部对 UIScrollView 的使用。但是……

    【讨论】:

      【解决方案7】:

      假设您可以找到手势识别器并删除它们是非常脆弱的。您正在使用关于 Apple 如何使用实现 UIPageViewController 在您的应用程序中提供功能的假设知识。如果这种情况发生了变化(例如在 iOS 5 和 iOS 6 之间),那么您的代码应用程序将开始以意想不到的方式运行。这几乎就像使用私有 API - 您无法保证它会与下一个操作系统版本一起使用。

      【讨论】:

      • 这将是一个很好的答案,除了文档对 UIPageViewController 的 gestureRecognizers 属性进行了以下说明:“这些手势识别器最初附加到页面视图控制器层次结构中的视图。更改屏幕区域用户可以使用手势导航,它们可以被放置在另一个视图中。”虽然删除它们可能不明智,但它们肯定是要被检索并可能移动的。
      • 我不知道,但如果如您所说,那么是的,显然 Apple 正在为您提供控制权并保证它将在操作系统版本之间保持可用(或被宣布弃用一段时间在被删除之前)。很好的收获。
      【解决方案8】:

      如果您使用 KVC(键值编码)方法访问识别器会发生什么? (我现在无法测试这个。)

      一个快速的测试可能是检索gestureRecognizers的数量:

      [self.pageViewController countOfKey:@"gestureRecognizers"];
      

      如果可行,您可能会进一步检索识别器数组:

      NSArray *recognizers = [self.pageViewController
                              mutableArrayValueForKey:@"gestureRecognizers"];
      

      瘦,但也许……

      编辑:终于可以测试了。使用以下:

      NSArray *pvcGRsNoKVC = [[NSArray alloc]
                             initWithArray:self.pageViewController.gestureRecognizers];
      NSArray *viewGRsNoKVC = [[NSArray alloc] initWithArray:self.view.gestureRecognizers];
      
      NSArray *pvcGRsKVC = [[NSArray alloc]
                           initWithArray:[self.pageViewController valueForKey:@"gestureRecognizers"]];
      NSArray *viewGRsKVC = [[NSArray alloc]
                            initWithArray:[self.view valueForKey:@"gestureRecognizers"]];
      

      没有任何区别。卷曲风格两种方式都很好。滚动样式显示数组不是零,而是空的。不过,有趣的是,视图 ALSO 并没有放弃它的识别器——尽管滚动功能在那里,所以它必须至少有一个平移识别器......

      【讨论】:

        【解决方案9】:

        另一种解决方案,仅用于历史。 您可以使任何视图成为一些手势识别器断路器,它应该在该视图的矩形中工作。必须有另一个带有委托的 UIPanGestureRecognizer。它可以是具有一种方法的任何对象:

        static UIPageViewController* getPageViewControllerFromView(UIView* v) {
            UIResponder* r = v.nextResponder;
            while (r) {
                if ([r isKindOfClass:UIPageViewController.class])
                    return (UIPageViewController*)r;
                r = r.nextResponder;
            }
            return nil;
        }
        
        - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
        {
            if (getPageViewControllerFromView(otherGestureRecognizer.view))
            {
                otherGestureRecognizer.enabled = NO;
                otherGestureRecognizer.enabled = YES;
            }
            return NO;
        }
        

        您可以使用以下类来破坏识别器:

        @interface GestureRecognizerBreaker : NSObject <UIGestureRecognizerDelegate>
        {
            UIGestureRecognizer* breaker_;
            BOOL(^needsBreak_)(UIGestureRecognizer*);
        }
        
        - (id) initWithBreakerClass:(Class)recognizerClass
                            checker:(BOOL(^)(UIGestureRecognizer* recognizer))needsBreak;
        
        - (void) lockForView:(UIView*)view;
        - (void) unlockForView:(UIView*)view;
        @end
        
        @implementation GestureRecognizerBreaker
        - (void) dummy:(id)r {}
        - (id) initWithBreakerClass:(Class)recognizerClass checker:(BOOL(^)(UIGestureRecognizer*))needsBreak {
            self = [super init];
            if (!self)
                return nil;
            NSParameterAssert([recognizerClass isSubclassOfClass:UIGestureRecognizer.class] && needsBreak);
            needsBreak_ = needsBreak;
            breaker_ = [[recognizerClass alloc] initWithTarget:self action:@selector(dummy:)];
            breaker_.delegate = self;
            return self;
        }
        
        - (BOOL) gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
        shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
            if (needsBreak_(otherGestureRecognizer)) {
                otherGestureRecognizer.enabled = NO;
                otherGestureRecognizer.enabled = YES;
            }
            return NO;
        }
        
        - (void) lockForView:(UIView*)view {
            [view addGestureRecognizer:breaker_];
        }
        
        - (void) unlockForView:(UIView*)view {
            [view removeGestureRecognizer:breaker_];
        }
        @end
        

        这适用于例如单音:

        static GestureRecognizerBreaker* PageViewControllerLocker() {
            static GestureRecognizerBreaker* i = nil;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                i = [[GestureRecognizerBreaker alloc]
                    initWithBreakerClass:UIPanGestureRecognizer.class
                    checker:^BOOL(UIGestureRecognizer* recognizer) {
                        UIView* v = recognizer.view;
                        UIResponder* r = v.nextResponder;
                        while (r) {
                            if ([r isKindOfClass:UIPageViewController.class])
                                return YES;
                            r = r.nextResponder;
                        }
                        return NO;
                    }];
            });
            return i;
        }
        

        调用 -lockForView 后:页面控制器的手势在拖入视图框架时不起作用。例如,我想锁定我的视图控制器的整个空间。所以在视图控制器方法的某个时刻我调用了

        [PageViewControllerLocker() lockForView:self.view];
        

        还有一点

        [PageViewControllerLocker() unlockForView:self.view];
        

        【讨论】:

          【解决方案10】:

          在 pageviewcontroller 内部有一个名为 quieingscrollview 的子视图,它有 3 个手势识别器来控制页面视图控制器 让他们使用

          [[pageViewController.view.subviews objectAtIndex:0] gestureRecognizers]
          

          【讨论】:

            【解决方案11】:

            在我的例子中,我的 UIPageViewController 上有一个 UIView“信息面板”,但是页面视图控制器的手势识别器干扰了通过我的信息面板的导航。

            我的解决方案是将 dataSource 设置为 nil,但也不允许页面视图控制器在信息面板启动时更新焦点:

            - (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
            {
                if (self.infoPanel) {
                    return NO;
                } else {
                    return YES;
                }
            }
            

            【讨论】:

              【解决方案12】:

              您可以使用UIGestureRecognizer 的委托方法来捕获和禁用任何手势例如,您可以使用此委托回调:gestureRecognizer:shouldReceiveTouch:。只需确保为所有识别器设置委托即可。

              【讨论】:

              • 问题不在于禁用手势识别器不起作用,而是我无法访问手势识别器,因为它们不是从具有滚动转换样式的 UIPageViewController 返回的。
              猜你喜欢
              • 2011-12-08
              • 1970-01-01
              • 1970-01-01
              • 2014-06-13
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-01-03
              相关资源
              最近更新 更多