【问题标题】:UIPageViewController Gesture recognizersUIPageViewController 手势识别器
【发布时间】:2011-12-08 23:33:06
【问题描述】:

我的 Viewcontroller 加载了 UIPageViewController。

视图控制器具有被 PageViewControllers 手势识别器覆盖的按钮。

例如,我在视图控制器的右侧有一个按钮,当您按下该按钮时,PageViewController 会接管并更改页面。

如何让按钮接收到触摸并取消 PageViewController 中的手势识别器?

我认为 PageViewController 使我的 ViewController 成为其视图的子视图。

我知道我可以关闭所有手势,但这不是我想要的效果。

我宁愿不要子类化 PageViewController,因为苹果说这个类不应该被子类化。

【问题讨论】:

  • 我也一直在开发使用 PageViewController 的应用程序......我正在寻找一种以编程方式更改页面的方法......你能帮我解决这个问题吗?
  • 当然。要以编程方式更改页面,您需要这样做:NSArray *viewControllers = [NSArray arrayWithObject:pageIWantToTurnTo]; 然后[pageViewController setViewControllers:viewControllers direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:NULL];
  • 有没有完成不为NULL的例子?
  • 我不明白。您在寻找最终产品吗?
  • @Rich86man 你能告诉我如何在页面视图控制器中显示多个视图控制器吗?我正在使用 UIPageViewController,但它再次重复相同的视图。你能帮我解决这个问题吗?

标签: ios uigesturerecognizer


【解决方案1】:

这是另一个解决方案,可以在 Xcode 模板的 self.view.gestureRecognizers = self.pageViewController.gestureRecognizers 部分之后添加到 viewDidLoad 模板中。它避免了弄乱手势识别器的胆量或处理它的代表。它只是从视图中删除轻击手势识别器,只留下滑动识别器。

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

// Find the tap gesture recognizer so we can remove it!
UIGestureRecognizer* tapRecognizer = nil;    
for (UIGestureRecognizer* recognizer in self.pageViewController.gestureRecognizers) {
    if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] ) {
        tapRecognizer = recognizer;
        break;
    }
}

if ( tapRecognizer ) {
    [self.view removeGestureRecognizer:tapRecognizer];
    [self.pageViewController.view removeGestureRecognizer:tapRecognizer];
}

现在要在页面之间切换,您必须滑动。 Taps 现在只能在页面视图顶部的控件上起作用(这就是我所追求的)。

【讨论】:

  • 我喜欢这种方法不会与设置手势识别器的代理混淆。
  • 更好的解决方案。但是,我最终启用和禁用了 tapRecognizer,因为在关闭子视图后我再次需要启用点击手势。
  • 非常简单。我更喜欢它。谢谢。
  • 响应者链的全部意义不在于您可以拥有处理程序并且它们可以做某事或不做某事吗?你不必只有一种方法来做某事?我希望看到一个解决方案,其中页面最右侧的点击被控件(或工具栏)忽略,并向上发送,因此它仍然可以触发分页。 [这就是响应者执行责任链的原因;这个解决方案只是把设计放在一边。]
  • 如果我们使用 UIPageViewControllerTransitionStyleScroll,self.pageViewController.gestureRecognizers 为空
【解决方案2】:

你可以覆盖

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
 shouldReceiveTouch:(UITouch *)touch

为了更好地控制 PageViewController 何时应该接收触摸而不是接收触摸。查看Dev API Gesture Recognizers中的“防止手势识别器分析触摸”

我的解决方案在 UIPageViewController 的 RootViewController 中如下所示:

在 viewDidLoad 中:

//EDITED Need to take care of all gestureRecogizers. Got a bug when only setting the delegate for Tap
for (UIGestureRecognizer *gR in self.view.gestureRecognizers) {
    gR.delegate = self;
}

覆盖:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    //Touch gestures below top bar should not make the page turn.
    //EDITED Check for only Tap here instead.
    if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
        CGPoint touchPoint = [touch locationInView:self.view];
        if (touchPoint.y > 40) {
            return NO;
        }
        else if (touchPoint.x > 50 && touchPoint.x < 430) {//Let the buttons in the middle of the top bar receive the touch
            return NO;
        }
    }
    return YES;
}

并且不要忘记将 RootViewController 设置为 UIGestureRecognizerDelegate。

(仅供参考,我只处于横向模式。)

编辑 - 上面的代码翻译成 Swift 2:

在 viewDidLoad 中:

for gr in self.view.gestureRecognizers! {
    gr.delegate = self
}

让页面视图控制器继承UIGestureRecognizerDelegate然后添加:

func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
    if let _ = gestureRecognizer as? UITapGestureRecognizer {
        let touchPoint = touch .locationInView(self.view)
        if (touchPoint.y > 40 ){
            return false
        }else{
            return true
        }
    }
    return true
}

【讨论】:

  • 谢谢,这解决了我的问题。在我的例子中,我希望无论触摸位置如何,点击手势都在我的完全控制之下,因此代码更加简单:它只检查gestureRecognizer 是否属于UITapGestureRecognizer 类。
  • 这个解决方案似乎有问题,至少在我的情况下。一旦我更改了手势识别器的代表,我开始每次viewControllerBeforeViewController 或viewControllerAfterViewController 返回 nil 时得到NSInvalidArgumentException(在最后一页表示)。异常原因:The number of view controllers provided (0) doesn't match the number required (2) for the requested transition.
  • @Kof NSInvalidArgumentExceptions 似乎只在 iOS 6 上使用 pagecurl 转换时发生。可以通过返回 [self viewControllerAtIndex:0] 而不是 nil(对于 viewControllerBeforeViewController)和 [self viewControllerAtIndex:maxpages] 而不是 nil(对于 viewControllerAfterViewController)来避免 - 一个不幸的副作用是额外的页面卷曲动画。
  • 在 iOS6 中 _pageViewController.gestureRecognizers(滚动效果)是空数组,您无法处理这些识别器。
【解决方案3】:

我遇到了同样的问题。示例和文档在 loadView 或 viewDidLoad 中执行此操作:

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

这将 UIViewControllers 视图中的手势识别器替换为 UIPageViewController 的手势识别器。现在,当触摸发生时,它们首先通过 pageViewControllers 手势识别器发送 - 如果它们不匹配,它们将被发送到子视图。

只需取消注释该行,一切都会按预期工作。

菲利普

【讨论】:

    【解决方案4】:

    将gestureRecognizers委托设置为viewController如下不再适用于ios6

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

    In ios6, setting your pageViewController's gestureRecognizers delegate to a viewController causes a crash

    【讨论】:

      【解决方案5】:

      在较新的版本中(我使用的是针对 iOS 8.1+ 的 Xcode 7.3),这些解决方案似乎都不起作用。

      接受的答案会因错误而崩溃:

      UIScrollView 的内置平移手势识别器必须将其滚动视图作为其委托。

      当前排名最高的答案(来自 Pat McG)不再有效,因为 UIPageViewController 的滚动视图似乎使用了您无法检查的奇怪手势识别器子类。因此,语句if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] ) 永远不会执行。

      我选择将每个识别器上的 cancelsTouchesInView 设置为 false,这样UIPageViewController 的子视图也可以接收触摸。

      在viewDidLoad:

      guard let recognizers = self.pageViewController.view.subviews[0].gestureRecognizers else {
          print("No gesture recognizers on scrollview.")
          return
      }
      
      for recognizer in recognizers {
          recognizer.cancelsTouchesInView = false
      }
      

      【讨论】:

        【解决方案6】:

        我用过

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

        允许点击进入 UIPageViewController 内的按钮

        【讨论】:

          【解决方案7】:

          就我而言,我想禁用对 UIPageControl 的点击,并让屏幕上的另一个按钮接收点击。滑动仍然有效。我尝试了很多方法,我相信这是最简单的工作解决方案:

          for (UIPageControl *view in _pageController.view.subviews) {
              if ([view isKindOfClass:[UIPageControl class]]) {
                  view.enabled = NO;
              }
          }
          

          这是从 UIPageController 子视图获取 UIPageControl 视图并禁用用户交互。

          【讨论】:

          • 这是最简单的解决方案,但不是启用,而是使用 isUserInteractionEnabled
          【解决方案8】:

          只需在您的 RootViewController 中创建一个子视图(链接到新的 IBOutlet 手势视图)并将手势分配给这个新视图。此视图覆盖了您希望启用手势的屏幕部分。

          在 viewDidLoad 变化中:

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

          到:

          self.gesturesView.gestureRecognizers = self.pageViewController.gestureRecognizers;
          

          【讨论】:

            【解决方案9】:

            如果您使用的是子类化的按钮,则可以覆盖 touchesBegan、touchesMoved 和 touchesEnded,酌情调用您自己的程序化翻页,但不调用 super 并将触摸传递到通知链。

            【讨论】:

            • Sataym,你是说不可能在 UIPageViewController 中放置按钮或交互元素吗? Apple 只是忘了在所有 UIPageViewController 文档中提及这一点?
            • 不看飞利浦的回答。除非我们注释那行代码,否则控件将不会接收触摸事件,除非它是第一次查看。注释那行代码后,它可以是任何自定义按钮或标签或 UIButton 或其他将接收触摸事件的东西。
            • 我也跑过这个。我在页面底部中间有一个按钮。这似乎行得通。但把它放在左边或右边不会。
            【解决方案10】:

            也可以使用这个(感谢帮助,说一下委托):

            // add UIGestureRecognizerDelegate
            
            NSPredicate *tp = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@", [UITapGestureRecognizer class]];
            UITapGestureRecognizer *tgr = (UITapGestureRecognizer *)[self.pageViewController.view.gestureRecognizers filteredArrayUsingPredicate:tp][0];
            tgr.delegate = self; // tap delegating
            
            NSPredicate *pp = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@", [UIPanGestureRecognizer class]];
            UIPanGestureRecognizer *pgr = (UIPanGestureRecognizer *)[self.pageViewController.view.gestureRecognizers filteredArrayUsingPredicate:pp][0];
            pgr.delegate = self; // pan delegating
            
            - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch 
            {
                CGPoint touchPoint = [touch locationInView:self.view];
            
                if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation]) && touchPoint.y > 915 ) {
                    return NO; // if y > 915 px in portrait mode
                }
            
                if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]) && touchPoint.y > 680 ) {
                    return NO; // if y > 680 px in landscape mode
                }
            
                return YES;
            }
            

            为我工作完美:)

            【讨论】:

              【解决方案11】:

              这是对我最有效的解决方案,我尝试了 JRAMER 答案很好,但在分页超出范围时会出现错误(对我来说是第 -1 页或第 23 页)

              PatMCG 解决方案没有给我足够的灵活性,因为它取消了所有的 taprecognizers,我仍然想要 tap 但不在我的标签内

              在我的 UILabel 中,我简单地覆盖如下,这个取消的点击仅适用于我的标签

              - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
              {
               if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
                   return NO;
               } else {
                   return YES;
               }
              }
              

              【讨论】:

                【解决方案12】:

                当我的用户跳转、卷曲和滑动到各种不同的页面视图时,我会定期创建页面视图控制器。在创建新的 pageviewcontroller 的例程中,我使用了上面显示的优秀代码的稍微简单的版本:

                UIPageViewController *npVC = [[UIPageViewController alloc]
                                       initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
                                       navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
                                       options: options];
                ...
                
                // Find the pageView tap gesture recognizer so we can remove it!
                for (UIGestureRecognizer* recognizer in npVC.gestureRecognizers) {
                    if ( [recognizer isKindOfClass:[UITapGestureRecognizer class]] ) {
                        UIGestureRecognizer* tapRecognizer = recognizer;
                        [npVC.view removeGestureRecognizer:tapRecognizer];
                       break;
                    }
                }
                

                现在点击可以按我的意愿工作(左右点击会跳转页面,卷发工作正常。

                【讨论】:

                  【解决方案13】:

                  用于删除点击识别器的 Swift 3 扩展:

                      import UIKit
                  
                  extension UIPageViewController {
                      func removeTapRecognizer() {
                          let gestureRecognizers = self.gestureRecognizers
                  
                          var tapGesture: UIGestureRecognizer?
                          gestureRecognizers.forEach { recognizer in
                              if recognizer.isKind(of: UITapGestureRecognizer.self) {
                                  tapGesture = recognizer
                              }
                          }
                          if let tapGesture = tapGesture {
                              self.view.removeGestureRecognizer(tapGesture)
                          }
                      }
                  }
                  

                  【讨论】:

                  • 在 iOS 12、Swift 4.2 上与我的手势不匹配
                  【解决方案14】:

                  我在寻找一种简单、全面的方法来响应 UIPageViewController 中的子视图控制器上的点击时,最终来到了这里。我的解决方案的核心(Swift 4、Xcode 9)就这么简单,在我的RootViewController.swift(与 Xcode 的“基于页面的应用程序”模板的结构相同)中:

                  override func viewDidLoad() {
                      super.viewDidLoad()
                  
                      ...
                  
                      let tapGesture = UITapGestureRecognizer(target: self, action: #selector(pageTapped(sender:)))
                      self.pageViewController?.view.subviews[0].addGestureRecognizer(tapGesture)
                  }
                  
                  ...
                  
                  @objc func pageTapped(sender: UITapGestureRecognizer) {
                      print("pageTapped")
                  }
                  

                  (我还使用了this answer 让我跟踪实际点击了哪个页面,即当前页面。)

                  【讨论】:

                    【解决方案15】:

                    我制定了一个可行的解决方案。 将另一个 UIGestureRecognizer 添加到 UIPageViewController 并实现下面提供的委托方法。 在您必须确定应该锁定或进一步传递哪个手势的每一刻,都会调用此方法。请记住提供对confictingView 的引用,在我的例子中是 UITableView,它也可以识别平移手势。这个视图被放置在 UIPageViewController 中,所以一个平移手势被识别了两次或者只是随机的方式。现在在这个方法中,我检查平移手势是否在我的 UITableView 和 UIPageViewController 中,我决定 UIPanGestureRecognizer 是主要的。

                    这种方法不会直接覆盖任何其他手势识别器,因此我们不必担心提到的“NSInvalidArgumentException”。

                    请记住,该模式实际上并未得到 Apple 的批准:)

                     var conflictingView:UIView?
                        func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
                            if otherGestureRecognizer.view === pageViewController?.view {
                    
                                if let view = conflictingView {
                                    var point = otherGestureRecognizer.location(in: self.view)
                                    if view.frame.contains(point) {
                                        print("Touch in conflicting view")
                                        return false
                                    }
                                }
                                print("Touch outside conficting view")
                                return true
                            }
                            print("Another view passed out")
                            return true
                        }
                    

                    【讨论】:

                      猜你喜欢
                      • 2012-10-17
                      • 1970-01-01
                      • 2012-01-03
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2023-03-04
                      • 2019-03-27
                      相关资源
                      最近更新 更多