UIPageViewController手势识别器

时间:2011-10-17 02:14:12

标签: ios uigesturerecognizer

我一直在申请一段时间,但由于NDA,我无法提出这个问题。

我的ViewController加载了UIPageViewController。视图控制器具有由PageViewControllers手势识别器覆盖的按钮。例如,我在viewcontroller的右侧有一个按钮,当你按下按钮时,PageViewController接管并更改页面。

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

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

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

更喜欢不要将PageViewController子类化,因为苹果称这个类不是要子类化的。

15 个答案:

答案 0 :(得分:54)

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

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

现在要在页面之间切换,您必须滑动。点击现在只适用于页面视图顶部的控件(这就是我所追求的)。

答案 1 :(得分:50)

您可以覆盖

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

答案 2 :(得分:4)

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

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

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

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

菲利普

答案 3 :(得分:3)

将gestureRecognizers委托设置为viewController,如下所示,不再适用于ios 6

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

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

答案 4 :(得分:3)

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

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

  

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

目前排名最高的答案(来自Pat McG)不再有效,因为UIPageViewController的scrollview似乎正在使用您无法检查的奇怪手势识别器子类。因此,语句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
}

答案 5 :(得分:2)

我用过

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

允许点击进入UIPageViewController内的按钮

答案 6 :(得分:0)

如果您正在使用已经创建子类的按钮,则可以覆盖touchesBegan,touchesMoved和touchesEnded,根据需要调用您自己的程序化页面转向,但不要调用super并将通知链传递给通知链。

答案 7 :(得分:0)

也可以使用这个(感谢您的帮助,关于委托说):

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

完美地为我工作:)

答案 8 :(得分:0)

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

在viewDidLoad中更改:

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

到:

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

答案 9 :(得分:0)

这是最适合我的解决方案我尝试了JRAMER的答案很好,除了我在分页超出界限时会出现错误(第-1页或第23页)

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

在我的UILabel中,我只是按照以下方式覆盖,仅取消了我的标签

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

答案 10 :(得分:0)

我定期创建pageviewcontrollers,因为我的用户会跳转,卷曲并滑动到各种不同的页面视图。在创建新的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;
    }
}

现在水龙头按照我的意愿工作(左右水龙头跳跃页面,卷发工作正常。

答案 11 :(得分:0)

用于删除点击识别器的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)
        }
    }
}

答案 12 :(得分:0)

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

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

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

答案 13 :(得分:0)

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

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让我跟踪实际点击的页面,即当前页面。)

答案 14 :(得分:0)

我制定了一个可行的解决方案。 将另一个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
    }