Pass touch to another view - ios

I have a UIScrollView as a subview in another UIScrollView I am trying to prevent scrolling from occurring in the subview and pass the touch to the superview.
- (void)viewDidLoad
{
self.outerScrollView = [[UIScrollView alloc] initWithFrame:self.view.bounds];
self.outerScrollView.showsVerticalScrollIndicator = NO;
self.gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanGesture:)];
self.gestureRecognizer.delegate = self;
[self.tableViewController.tableView addGestureRecognizer:self.gestureRecognizer];
[self.outerScrollView addSubview:self.tableViewController.tableView];
[self.view addSubview:self.outerScrollView];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (void)handlePanGesture:(UIPanGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateBegan) {
if (gesture.view == self.tableViewController.tableView) {
self.tableViewController.tableView.contentOffset = CGPointMake(0, 0);
// Pass to self.outerScrollView
// Trying to make self.outerScrollView take over the touch
}
}
}

Related

The action of GestureRecognizer does not fire

I add the gesture to view as following steps:
#interface iCarousel () <UIGestureRecognizerDelegate>
{
UIPanGestureRecognizer * mPanGesture;
UITapGestureRecognizer * mTapGesture;
}
#end
#implementation iCarousel
- (void)setUp
{
_contentView = [[UIView alloc] initWithFrame:self.bounds];
_contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
/
mPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(iCarouselDidPan:)];
mPanGesture.delegate = self;
[_contentView addGestureRecognizer:mPanGesture];
/
mTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(iCarouselDidTap:)];
mTapGesture.delegate = self;
[_contentView addGestureRecognizer:mTapGesture];
/
self.accessibilityTraits = UIAccessibilityTraitAllowsDirectInteraction;
self.isAccessibilityElement = YES;
[self addSubview:_contentView];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if ((self = [super initWithCoder:aDecoder]))
{
[self setUp];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
[self setUp];
}
return self;
}
...
- (void)iCarouselDidTap:(UITapGestureRecognizer *)tapGesture
{
...
}
- (void)iCarouselDidPan:(UIPanGestureRecognizer *)panGesture
{
...
}
...
#end
Now, the method in this class
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gesture
return me YES
But, the actions iCarouselDidPan: and iCarouselDidTap: do not fire.
The information is printed in gestureRecognizerShouldBegin: method.
(lldb) po _contentView.gestureRecognizers
<__NSArrayI 0x1572bec0>(
<UIPanGestureRecognizer: 0x16dea750; state = Possible; view = <UIView 0x16dea640>; target= <(action=iCarouselDidPan:, target=<iCarousel 0x16dea1b0>)>>,
<UITapGestureRecognizer: 0x16e8f310; state = Possible; view = <UIView 0x16dea640>; target= <(action=iCarouselDidTap:, target=<iCarousel 0x16dea1b0>)>>
)
(lldb) po gesture
<UIPanGestureRecognizer: 0x16dea750; state = Possible; view = <UIView 0x16dea640>; target= <(action=iCarouselDidPan:, target=<iCarousel 0x16dea1b0>)>>
It's only happened on devices <4s 9.3.x , 5 10.3.1> at current moment.
Try implement the following methods of protocol UIGestureRecognizerDelegate inside your iCarousel:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch {
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceivePress:(UIPress *)press {
return YES;
}
Possible cases for not working gestures .
1 - You need to enable interaction of view.
[_contentView setUserInteractionEnabled:YES];
2 - Add this delegate in your class.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
Declare UIGestureRecognizerDelegate in .h file
Gesture definition
-(UITapGestureRecognizer*)setTapGesture{
UITapGestureRecognizer *Tap = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleTapWithGestureRecognizer:)];
Tap.delegate = self;
return Tap;
}
-(void)handleTapWithGestureRecognizer:(UITapGestureRecognizer *)TapGestureRecognizer{
NSLog(#"tap Gesture");
}
-(UIPanGestureRecognizer*)setpanGesture{
UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
[panRecognizer setMinimumNumberOfTouches:1];
[panRecognizer setMaximumNumberOfTouches:1];
return panRecognizer;
}
- (void)handlePan:(UIPanGestureRecognizer *)recognizer {
[self.view bringSubviewToFront:recognizer.view];
CGPoint touchLocation = [recognizer locationInView:self.view];
self.YOURVIEW.center = touchLocation;
}
Call Gesture or add gesture to your view
[self.YOURVIEW addGestureRecognizer:[self setTapGesture]];
[self.YOURVIEW addGestureRecognizer:[self setpanGesture]];
self.YOURVIEW.userInteractionEnabled = YES;
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
Good Luck
Happy Coding.

Tap Gesture subview touch detect

i am creating drawer
self.isShowMenuVC = NO;
_menuView = [MenuViewController viewController];
[self.menuView setDelegate:self];
[self addChildViewController:self.menuView];
[self.menuView.view setFrame:CGRectMake(-kMenuTableWidth, 0, kMenuTableWidth, self.view.frame.size.height)];
[self.view addSubview:self.menuView.view];
[self.menuView didMoveToParentViewController:self];
UITapGestureRecognizer *outsideTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(outsideTapped:)];
[self.view addGestureRecognizer:outsideTap];
outsideTap.delegate = self;
and when button tap i just set frame of _menuView.view to behave like a drawer
what i want is to detect touch outside of drawer but i am not able to do it
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (touch.view == self.menuView.view) {
NSLog(#"Touch Drawer");
} else {
NSLog(#"Touch Outside");
}
return YES;
}
but it is always show Touch Outside"
i am missing something but don't know what thanks in advance
Also try with 2 gesture but not working because one gesture in self.view so, when i tap in drawer method call 2 times.
for that i tried to disable one gesture, still calling two times
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (gestureRecognizer == self.touchInDrawer) {
NSLog(#"Touch in drawer");
[self.touchOutSideDrawer setEnabled:NO];
} else {
NSLog(#"Outside");
[self hideMenuView];
}
return YES;
}
The UITapGestureRecognizer cannot detect the touch outside the view which it belongs to.
You need to create another UITapGestureRecognizer and add them to self.menuView.view.
Also you can make two #property for your gesture recognizers and check them inside method:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (gestureRecognizer == self.firstGesture) {
NSLog(#"Touch in first gesture");
} else {
NSLog(#"Touch in another gesture");
}
I have solve this issue with adding two gesture
#property (strong,nonatomic) UITapGestureRecognizer *touchInDrawer;
#property (strong,nonatomic) UITapGestureRecognizer *touchOutSideDrawer;
as per #Eugene Zaychenko's Answer but there is still problem because delegate method calling two times
also i can't [self.touchOutSideDrawer setEnabled:NO]; when touch in drawer tapped, because after that it will remove from the view and never executed again if [self.touchOutSideDrawer setEnabled:YES];
but most interesting thing is
_touchInDrawer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(outsideTapped:)];
[self.menuView.view addGestureRecognizer:self.touchInDrawer];
self.touchInDrawer.delegate = self;
_touchOutSideDrawer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(outsideTapped:)];
[self.view addGestureRecognizer:self.touchOutSideDrawer];
self.touchOutSideDrawer.delegate = self;
outsideTapped method calling only one time so i shifted my all code there and is working
- (void) outsideTapped:(UITapGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer == self.touchOutSideDrawer) {
// [self.view removeGestureRecognizer:self.touchOutSideDrawer];
[self hideMenuView];
NSLog(#"Outside");
} else {
NSLog(#"Touch in drawer");
// [self.touchOutSideDrawer setEnabled:NO];
}
}

exclude subview from UIPinchGestureRecognizer

UIView *superview = [[UIView alloc] initWithFrame:CGRectMake:(0, 0, 320, 480);
UIView *subview = [[UIView alloc] initWithFrame:CGRectMake:(0, 0, 320, 480);
UIPanGestureRecognizer *recognizer = [[UIPinchGestureRecognizer alloc] initWithTarget: self action: #selector(handlePinch);
superview.userInteractionEnabled = YES;
subview.userInteractionEnabled = YES;
[superview addGestureRecognizer:recognizer];
I have a subview overlapping on top of a superview. The subview has 4 buttons that needs to be tappable. The superview has a pinch-zoom gesture that zooms the view. But i would like to disable the zoom on the subview.
The recognizer is fired inside the subview as well, is there a way to exclude the recognizer from the subview?
I used the simple way below:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if (touch.view != subview) { // accept only touchs on superview, not accept touchs on subviews
return NO;
}
return YES;
}
UIView *superview = [[UIView alloc] initWithFrame:CGRectMake:(0, 0, 320, 480);
superview.tag=1; //set Tag for superview
UIView *subview = [[UIView alloc] initWithFrame:CGRectMake:(0, 0, 320, 480);
subview.tag==2; //set Tag for subview
UIPanGestureRecognizer *recognizer = [[UIPinchGestureRecognizer alloc] initWithTarget: self action: #selector(handlePinch);
superview.userInteractionEnabled = YES;
subview.userInteractionEnabled = YES;
[superview addGestureRecognizer:recognizer];
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
{
if(touch.view .tag==2) //if subview is touched then disable pinch gesture.
{
return NO;
}
return YES;
}
This might not be the best option, but this could also work? Other StackOverflow users please be kind, I'm new!
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
shouldReceiveTouch:(UITouch *)touch
{
CGPoint touchPoint = [gestureRecognizer locationInView: superview];
if(CGRectContainsPoint(subview.frame, touchPoint)
{
return NO;
}
return YES;
}
Apple Docs containing CGRectContainsPoint.
https://developer.apple.com/library/mac/documentation/GraphicsImaging/Reference/CGGeometry/index.html#//apple_ref/c/func/CGRectContainsPoint

UIPageViewController ans UISwipeGestureRecognizer

I have several pages on UIPageViewController. Each page is created from its own view, that has nested views. One of them is image. I want to have contol made in the way, that user can change pages by swipe on everything but image. Swipe on image will change images and not the page.
How can I achieve this? I have added UISwipeGestureRecognizer to the image, set userInteraction to YES, but swipe is send through and cause page turn.
I have this code in view load method (awakeFromNib)
UISwipeGestureRecognizer *swipeGestureRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(changeImageRight)];
[swipeGestureRight setDirection:UISwipeGestureRecognizerDirectionRight];
swipeGestureRight.delegate = self;
swipeGestureRight.numberOfTouchesRequired = 1;
[self.image addGestureRecognizer:swipeGestureRight];
Something like this should do the trick for you :
- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
if (gestureRecognizer == self){
if ([otherGestureRecognizer isMemberOfClass:self.class]){
if ([self isGestureRecognizerInSuperviewHierarchy:otherGestureRecognizer]){
return YES;
} else if ([self isGestureRecognizerInSiblings:otherGestureRecognizer]){
return YES;
}
}
}
return NO;
}
- (BOOL) isGestureRecognizerInSiblings:(UIGestureRecognizer *)recognizer{
UIView *superview = self.view.superview;
NSUInteger index = [superview.subviews indexOfObject:self.view];
if (index != NSNotFound){
for (int i = 0; i < index; i++){
UIView *sibling = superview.subviews[i];
for (UIGestureRecognizer *viewRecognizer in sibling.gestureRecognizers){
if (recognizer == viewRecognizer){
return YES;
}
}
}
}
return NO;
}
- (BOOL) isGestureRecognizerInSuperviewHierarchy:(UIGestureRecognizer *)recognizer{
if (!recognizer) return NO;
if (!self.view) return NO;
//Check siblings
UIView *superview = self.view;
while (YES) {
superview = superview.superview;
if (!superview) return NO;
for (UIGestureRecognizer *viewRecognizer in superview.gestureRecognizers){
if (recognizer == viewRecognizer){
return YES;
}
}
}
}
- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
if ([gestureRecognizer respondsToSelector:#selector(gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:)]){
return [(id <UIGestureRecognizerDelegate>)gestureRecognizer gestureRecognizer:gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:otherGestureRecognizer];
}
return NO;
}

gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer crashing

I have a gesture recognizer on my view controller which I use to slide in/out the navigation bar. Unfortunately there is a bad NSZombie that makes the whole app crash when I go swipe to go to the previous controller. It's very difficult to track because it only happens in a specific view controller.
Here is the error:
[MyViewController gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:]: message sent to deallocated instance
And here is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UIPanGestureRecognizer *pgr = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(pgr:)];
pgr.delegate = self;
[self.view addGestureRecognizer:pgr];
}
- (void)pgr:(UIPanGestureRecognizer *)gesture {
// Check if this is the first touch
if (gesture.state == UIGestureRecognizerStateBegan) {
CGPoint point = [gesture locationInView:gesture.view];
self.start = point.y;
}
CGPoint point = [gesture locationInView:gesture.view];
self.currentX = point.x;
self.offsetY = point.y - self.start;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
if ([gestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]]) {
return YES;
}
return NO;
}
The delegate method is
- (BOOL)shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
Try to use
- (BOOL)shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([otherGestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]]) {
return YES;
}
return NO;
}

Resources