UIPageViewController在iOS 6中不返回手势识别器
我正在尝试禁用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转换工作正常。
这种行为在雷达中存在一个错误 。 所以,我敢打赌,直到苹果修复它,将没有机会解决这个问题。
我想到的一个解决方法是在UIPageViewController
顶部放置一个透明的子视图,并添加一个UIPanGestureRecognizer
来拦截这种手势,而不是进一步转发。 当禁用手势时,您可以启用此视图/识别器。
我尝试了泛和手势识别器的组合,它的工作原理。
这是我的testing代码:
- (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被识别。
我知道这是破解,但面对UIPageViewController
的一个看似错误(至less,实际行为明显不同于参考状态),我看不到任何更好的解决scheme。
在UIPageViewController.h中find这个:
//如果转换样式是'UIPageViewControllerTransitionStylePageCurl',则只填充。 @属性(非primefaces,只读)NSArray * gestureRecognizers;
所以,不是一个错误 – 在devise滚动样式时,pageViewController没有得到手势识别器。
您始终可以尝试在页面视图控制器的子视图上禁用用户交互:
for (UIScrollView *view in self.pageViewController.view.subviews) { if ([view isKindOfClass:[UIScrollView class]]) { view.scrollEnabled = NO; } }
根据UIPageViewController头文件,当数据源为零时,手势导航被禁用。
因此,当您要禁用滑动时将数据源设置为零,然后当您要启用滑动时,请重置数据源。
即
// turns off paging pageViewController.datasource = nil // turns on paging pageViewController.datasource = self;
您可以通过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:)]; } }
如果UIPageViewControllers UIPanGestureRecognizer正在吃掉/吞下远离其他PanGestureRecognizer的所有事件(例如滑动菜单)。
您可以轻松展开Bernies解决scheme,并使UIScrollViews PanGestureRecognizer需要其他识别器失败。 像这样的东西:
for (UIView *view in pageViewController.view.subviews) { if ([view isKindOfClass:[UIScrollView class]]){ UIScrollView *scrollView = (UIScrollView *)view; [scrollView.panGestureRecognizer requireGestureRecognizerToFail:otherRecognizer]; } }
这样,滚动的PanGestureRecognizer只会在我想要的地方触发。
这可能不是最好的未来certificate解决scheme,因为我们要求Apple不要更改UIPageViewControllers内部使用的UIScrollView。 但…
假设你可以find手势识别器,并删除它们非常脆弱。 您正在使用苹果公司如何使用UIPageViewController在您的应用程序中提供function的假设知识。 如果这个变化(例如iOS 5和iOS 6之间),那么你的代码应用程序将开始以意想不到的方式行事。 这几乎就像使用一个私人的API – 你不能保证它将与下一个操作系统版本。
如果使用KVC(Key Value Coding)方法来访问识别器会发生什么? (我现在不能在这里testing。)
快速testing可能是检索gestureRecognizers的数量:
[self.pageViewController countOfKey:@"gestureRecognizers"];
如果这样做,你可能会进一步检索识别器数组:
NSArray *recognizers = [self.pageViewController mutableArrayValueForKey:@"gestureRecognizers"];
薄,但也许…
编辑:是能够最终testing。 使用以下内容:
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"]];
它没有任何区别。 curl风格工作都很好, 滚动样式显示数组不是零,而是空的。 不过有趣的是,这个视图还没有放弃它的识别器 – 尽pipe滚动function在那里,所以它必须至less有一个泛识别器。
另一个解决scheme,只为历史。 你可以使任何视图成为一些手势识别器的断路器,它应该在该视图的矩形中工作。 代理必须有另一个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
这是例如作为singletone:
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之后:页面控制器的手势在拖动视图框架时不起作用。 例如,我想locking我的视图控制器的整个空间。 所以在视图控制器方法中的某个地方我打电话
[PageViewControllerLocker() lockForView:self.view];
另一点
[PageViewControllerLocker() unlockForView:self.view];
在pageviewcontroller里面有一个名为quieingscrollview的子视图,它有3个手势识别器,它控制页面视图控制器来让它们使用
[[pageViewController.view.subviews objectAtIndex:0] gestureRecognizers]
在我的情况下,我有一个UIView“信息面板”,通过我的UIPageViewController,但页面视图控制器的手势识别器干扰导航通过我的信息面板。
我的解决scheme是将dataSource设置为零,但也不允许页面视图控制器在信息面板启动时更新焦点:
- (BOOL)shouldUpdateFocusInContext:(UIFocusUpdateContext *)context { if (self.infoPanel) { return NO; } else { return YES; } }
您可以使用UIGestureRecognizer
的委托方法来捕获和禁用任何手势。例如,您可以使用此委托callback: gestureRecognizer:shouldReceiveTouch:
只要确保为所有的识别器设置代表。