当我的滚动视图不在最左边时,如何防止 UINavigationController 识别滑动?
How do I prevent a UINavigationController from recognising swipes when my scroll view isn't at the far left?
在我的应用程序中,许多视图控制器在它们的视图中都有一个可以水平滚动的 UIScrollView,它们位于 UINavigationController 堆栈中。有时,当用户在这些视图上从左到右滚动时,他们会意外触发导航控制器的滑动手势并将当前控制器从堆栈中弹出。
这些控制器的视图都来自一个共同的 class。
我想要的是,如果滚动视图可以从左向右滚动,那每次都会发生;但如果不是,则允许导航控制器的手势发生。
我怎样才能集中实现这一点?我很乐意覆盖 UINavigationController 或我的共同视图 class,但我不想在每个控制器 class.
中向 viewWill/DidAppear/Disappear 添加内容
感谢 Pete Steinberger,首先我们创建一个代理,这样我们就可以拦截导航控制器的委托消息:
#import <objc/runtime.h>
@interface SGBGestureDelegateProxy : NSProxy <UIGestureRecognizerDelegate>;
@property (nonatomic, weak, readonly) UINavigationController *navigationController;
@property (nonatomic, weak, readonly) id delegate;
- (id)initWithNavigationController:(UINavigationController *)navigationController delegate:(id)delegate;
@end
@implementation SGBGestureDelegateProxy
- (id)initWithNavigationController:(UINavigationController *)navigationController delegate:(id)delegate
{
if (self)
{
_navigationController = navigationController;
_delegate = delegate;
objc_setAssociatedObject(delegate, _cmd, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return self;
}
- (BOOL)respondsToSelector:(SEL)selector
{
return [self.delegate respondsToSelector:selector];
}
- (id)forwardingTargetForSelector:(SEL)selector
{
return [self.delegate respondsToSelector:selector] ? self.delegate : self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return [self.delegate methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
// ignore
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
UIView *topView = self.navigationController.topViewController.view;
CGPoint topPoint = [touch locationInView:topView];
for (UIView *view = [topView hitTest:topPoint withEvent:nil]; view; view = view.superview)
{
UIScrollView *scrollView = (UIScrollView *)view;
if ([scrollView isKindOfClass:[UIScrollView class]] && scrollView.scrollEnabled && (scrollView.contentOffset.x != 0))
{
return NO;
}
}
return [self.delegate gestureRecognizer:gestureRecognizer shouldReceiveTouch:touch];
}
@end
然后我们将代理插入导航控制器的手势识别器 viewDidLoad
:
- (void)viewDidLoad
{
[super viewDidLoad];
id delegate = self.interactivePopGestureRecognizer.delegate;
id proxy = [[C360NavigationControllerGestureDelegateProxy alloc] initWithNavigationController:self delegate:delegate];
self.interactivePopGestureRecognizer.delegate = proxy;
}
...我们完成了。每当用户开始滑动时,如果滑动开始于水平滚动的滚动视图,则代理拦截 gestureRecognizer:shouldReceiveTouch:
和 returns NO。如果滚动视图没有滚动,或者没有滚动视图,那么手势被识别为正常。
在我的应用程序中,许多视图控制器在它们的视图中都有一个可以水平滚动的 UIScrollView,它们位于 UINavigationController 堆栈中。有时,当用户在这些视图上从左到右滚动时,他们会意外触发导航控制器的滑动手势并将当前控制器从堆栈中弹出。
这些控制器的视图都来自一个共同的 class。
我想要的是,如果滚动视图可以从左向右滚动,那每次都会发生;但如果不是,则允许导航控制器的手势发生。
我怎样才能集中实现这一点?我很乐意覆盖 UINavigationController 或我的共同视图 class,但我不想在每个控制器 class.
中向 viewWill/DidAppear/Disappear 添加内容感谢 Pete Steinberger,首先我们创建一个代理,这样我们就可以拦截导航控制器的委托消息:
#import <objc/runtime.h>
@interface SGBGestureDelegateProxy : NSProxy <UIGestureRecognizerDelegate>;
@property (nonatomic, weak, readonly) UINavigationController *navigationController;
@property (nonatomic, weak, readonly) id delegate;
- (id)initWithNavigationController:(UINavigationController *)navigationController delegate:(id)delegate;
@end
@implementation SGBGestureDelegateProxy
- (id)initWithNavigationController:(UINavigationController *)navigationController delegate:(id)delegate
{
if (self)
{
_navigationController = navigationController;
_delegate = delegate;
objc_setAssociatedObject(delegate, _cmd, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return self;
}
- (BOOL)respondsToSelector:(SEL)selector
{
return [self.delegate respondsToSelector:selector];
}
- (id)forwardingTargetForSelector:(SEL)selector
{
return [self.delegate respondsToSelector:selector] ? self.delegate : self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return [self.delegate methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
// ignore
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
UIView *topView = self.navigationController.topViewController.view;
CGPoint topPoint = [touch locationInView:topView];
for (UIView *view = [topView hitTest:topPoint withEvent:nil]; view; view = view.superview)
{
UIScrollView *scrollView = (UIScrollView *)view;
if ([scrollView isKindOfClass:[UIScrollView class]] && scrollView.scrollEnabled && (scrollView.contentOffset.x != 0))
{
return NO;
}
}
return [self.delegate gestureRecognizer:gestureRecognizer shouldReceiveTouch:touch];
}
@end
然后我们将代理插入导航控制器的手势识别器 viewDidLoad
:
- (void)viewDidLoad
{
[super viewDidLoad];
id delegate = self.interactivePopGestureRecognizer.delegate;
id proxy = [[C360NavigationControllerGestureDelegateProxy alloc] initWithNavigationController:self delegate:delegate];
self.interactivePopGestureRecognizer.delegate = proxy;
}
...我们完成了。每当用户开始滑动时,如果滑动开始于水平滚动的滚动视图,则代理拦截 gestureRecognizer:shouldReceiveTouch:
和 returns NO。如果滚动视图没有滚动,或者没有滚动视图,那么手势被识别为正常。