I am working on an iphone (iOS 4.0 or later) app and having some troubles with touch handling between multiple views. I am having a view structure like this
If i undestand your problem correct, you may just add another, clear view with rect, same as you A and B view, and implement all gesture on it: when you do pinch gesture, control subView A, when swipe and tap (single and double) gestures - control subView B. You can do it different ways: via pointers or just sending recived gesture to method in class, wich controls your sub view.
for example:
UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)];
[leftSwipe setDirection:(UISwipeGestureRecognizerDirectionLeft)];
leftSwipe.delegate = subViewAcontroller;
[clearView addGestureRecognizer:leftSwipe];
[leftSwipe release];
UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(rightSwipe:)];
[rightSwipe setDirection:(UISwipeGestureRecognizerDirectionRight)];
rightSwipe.delegate = subViewAcontroller;
[clearView addGestureRecognizer:rightSwipe];
[rightSwipe release];
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinch:)];
pinch.delegate = subViewBcontroller;
[clearView addGestureRecognizer:pinch];
[pinch release];
or:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
if ([gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]]) {
NSLog(@"pinchGesture");
[subViewBcontroller solvePinchGesture: gestureRecognizer];
}
//etc
return YES;
}
The pinch gesture will Never received by the aView, because you view hierarchy is incorrect.
---> A superView
|
---> SubView - A
|
---> SubView - B (exactly on top of A, completely blocking A).
bView will captures all multi-touches including the pinch gesture, but bView will not handle the pinch gesture, so it will pass it to Next responder in the Responder chain, i.e. the superView of bView or the viewController of bView. The pinch event will never passed to its sibling views, because they are in parallel (no parent-child or view-viewController relationship).
You can change your view hierarchy as below:
---> A superView
|
---> SubView - A
|
---> SubView - B (child view of b, exactly on top of A, completely blocking A)
- (void)viewDidLoad {
[super viewDidLoad];
aView = [[UIView alloc] initWithFrame:self.view.bounds];
aView.backgroundColor = [UIColor blueColor];
bView = [[UIView alloc] initWithFrame:self.view.bounds];
bView.backgroundColor = [UIColor redColor];
[self.view addSubview:aView];
[aView addSubview:bView];
UISwipeGestureRecognizer *leftSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(leftSwipe:)];
[leftSwipe setDirection:(UISwipeGestureRecognizerDirectionLeft)];
leftSwipe.delegate = self;
[bView addGestureRecognizer:leftSwipe];
UISwipeGestureRecognizer *rightSwipe = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(rightSwipe:)];
[rightSwipe setDirection:(UISwipeGestureRecognizerDirectionRight)];
rightSwipe.delegate = self;
[bView addGestureRecognizer:rightSwipe];
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinch:)];
pinch.delegate = self;
[aView addGestureRecognizer:pinch];
}
Please get more information from below link: https://developer.apple.com/library/ios/#documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/EventsiPhoneOS/EventsiPhoneOS.html#//apple_ref/doc/uid/TP40009541-CH2-SW1
--> Important note of the event passing in the responder chain "When the system delivers a touch event, it first sends it to a specific view. For touch events, that view is the one returned by hitTest:withEvent:; for “shaking”-motion events, remote-control events, action messages, and editing-menu messages, that view is the first responder. If the initial view doesn’t handle the event, it travels up the responder chain along a particular path:
my answer above is not the only solution, what you need to do is make swipe and pinch gesture handling logics in the same responder chain, so the un-handle pinch gesture event will passed to the correct responder. For example, you can omit subview b, and add the swipe left/right gestures to superView of aView.
I would suggest you to use below line of code where needed may help you out.
//--->for bview gesture to detect swipe
[pinch requireGestureRecognizerToFail:leftSwipe]; &&
[pinch requireGestureRecognizerToFail:rightSwipe];
//--->for aview gesture to detect pinch
[rightSwipe requireGestureRecognizerToFail:rightSwipe]; ||
[leftSwipe requireGestureRecognizerToFail:pinch];
If bView has pinch gesture. From bView's pinch gesture action if called aView's pinch gesture action then...
UIPinchGestureRecognizer *pinchA = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinchA:)];
[aView addGestureRecognizer:pinchA];
[pinchA release];
UIPinchGestureRecognizer *pinchB = [[UIPinchGestureRecognizer alloc]initWithTarget:self action:@selector(handlePinchB:)];
[bView addGestureRecognizer:pinchB];
[pinchB release];
-(void)handlePinchA:(UIGestureRecognizer*)gestureRecognizer
{
NSLog(@"handlePinch on A");
}
-(void)handlePinchB:(UIGestureRecognizer*)gestureRecognizer
{
NSLog(@"handlePinch on B");
[self handlePinchA:gestureRecognizer];
}
aView is a thirdParty lib. Can you call handlePinchA method from outside? NSLog gestureRecognizers prints me action=handlePinchA
NSLog(@"gestureRecognizers %@", [aView gestureRecognizers]);
Prints me:
gestureRecognizers (
"<UIPinchGestureRecognizer: 0x8b3e500; state = Possible; view = <UIView 0x8b2f470>; target= <(action=handlePinchA:, target=....
I did something similar some time ago. I wanted to do two different things with one finger and two or more fingers. My solution might need some tweak for you and i can't ensure everything will work exactly the same in your code-environment. I did not find proper documentation of hitTest and why it is called multiple times in a row. So i did some testing and came to a solution, how to separate one and multi-finger gestures, which worked very well for my needs.
I had this view hierarchy:
---> A superView
|
---> ScrollView - A - all touches
|
---> ScrollView - B - only two+ finger scroll (exactly on top of A, completely blocking A).
I implemented the switch in hitTest of the superView:
BOOL endDrag; // indicates scrollviewB did finish
BOOL shouldClearHitTest; // bool to help calling the method clearHitTest only once.
BOOL multiTouch; // needed for the method clearHitTest to know if a multi-touch is detected or not
NSMutableArray *events; // store all events within one eventloop/touch;
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if (!endDrag) {
if (!shouldClearHitTest) { // as hitTest will be called multible times in one event-loop and i need all events, i will clean the temporaries when everything is done
shouldClearHitTest = YES;
[[NSRunLoop mainRunLoop] performSelector:@selector(clearHitTest) target:self argument:nil order:1 modes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
}
[events addObject:event]; // store the events so i can access them after all hittests for one touch are evaluated
if (event.type == UIEventTypeTouches && ([_events count] > 3 || [[event allTouches] count] > 0 || _currentEvent)) { // two or more fingers detected. at least for my view hierarchy
multiTouch = YES;
return scrollViewB;
}
}else {
endDrag = NO;
}
return scrollViewA;
}
- (void)clearHitTest {
if (shouldClearHitTest) {
shouldClearHitTest = NO;
[events removeAllObjects];
if (multiTouch) {
// Do some special stuff for multiTouch
}
multiTouch = NO;
}
}