35

我正在尝试禁用 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,因此我无法访问手势识别器。

有什么办法可以解决这个问题吗?我不认为我做错了什么,因为卷曲过渡工作正常。

4

12 回答 12

32

在 UIPageViewController.h 中找到了这个:

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

因此,这不是一个错误 - 根据设计,当设置滚动样式时 pageViewController 不会获得手势识别器。

于 2012-11-10T17:50:21.993 回答
28

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

for (UIScrollView *view in self.pageViewController.view.subviews) {
    if ([view isKindOfClass:[UIScrollView class]]) {
        view.scrollEnabled = NO;
    }
}
于 2013-07-11T00:09:27.370 回答
26

雷达中有一个针对此行为的错误。所以,我敢打赌,在苹果修复它之前,没有机会解决这个问题。

我想到的一种解决方法是在您的顶部放置一个透明的子视图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(至少,实际行为与参考状态明显不同),我看不到任何更好的解决方案。

于 2012-11-08T16:48:21.230 回答
19

根据 UIPageViewController 头文件,当数据源为 nil 时,手势驱动导航被禁用。

因此,当您想要禁用滑动时,将 datasource 设置为 nil,然后当您想要启用滑动时,重置数据源。

IE

// turns off paging
pageViewController.datasource = nil

// turns on paging
pageViewController.datasource = self;
于 2013-11-13T20:25:23.627 回答
9

您可以通过 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:)];
    }
}
于 2013-10-09T08:00:24.670 回答
2

如果 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 的使用。但...

于 2014-01-15T15:03:24.270 回答
1

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

于 2012-11-08T17:01:52.863 回答
1

如果您使用 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"]];

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

于 2012-11-08T17:21:30.340 回答
0

另一种解决方案,仅用于历史。您可以使任何视图成为一些手势识别器断路器,它应该在该视图的矩形中工作。必须有另一个带有委托的 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];
于 2013-10-10T08:49:24.820 回答
0

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

[[pageViewController.view.subviews objectAtIndex:0] gestureRecognizers]
于 2015-08-20T08:34:35.460 回答
0

就我而言,我的 UIPageViewController 上有一个 UIView“信息面板”,但页面视图控制器的手势识别器干扰了通过我的信息面板进行的导航。

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

- (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context
{
    if (self.infoPanel) {
        return NO;
    } else {
        return YES;
    }
}
于 2017-03-01T06:26:58.587 回答
-1

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

于 2012-10-27T23:49:42.673 回答