How do I use two actions in a UIPanGestureRecognizer? - objective-c

I am working with two subviews. Each will be unique and have it's own "action".
Subview 1 = User can drag around the view, rotate, and zoom it
Subview 2 = When user moves finger across their screen an image is added at each point their finger touches.
I have both of these completed by using UIPanGestureRecognizer. My question is, how can I separate these two actions? I want to be able to add one subview, do what is required, and then when I add the other subview, prevent the previous actions from occurring.
Here is what I have tried, this is done in my panGesture method:
for (UIView * subview in imageView.subviews)
{
if ([subview isKindOfClass:[UIImageView class]])
{
if (subview == _aImageView)
{
CGPoint translation = [panRecognizer translationInView:self.view];
CGPoint imageViewPosition = _aImageView.center;
imageViewPosition.x += translation.x;
imageViewPosition.y += translation.y;
_aImageView.center = imageViewPosition;
[panRecognizer setTranslation:CGPointZero inView:self.view];
}
else if (subview == _bImageView)
{
currentTouch = [panRecognizer locationInView:self.view];
CGFloat distance = [self distanceFromPoint:currentTouch ToPoint:prev_touchPoint];
accumulatedDistance += distance;
CGFloat fixedDistance = 60;
if ([self distanceFromPoint:currentTouch ToPoint:prev_touchPoint] > fixedDistance)
{
[self addbImage];
prev_touchPoint = currentTouch;
}
}
}
}

If you want different gesture recognition in two different views, put separate recognizers on each view.

Usually, you want to have your view controller own and manage gesture recognizers, e.g.
- (void)viewDidLoad {
self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
self.panGesture.delegate = self;
[self.viewX addGestureRecognizer:self.panGesture];
// repeat with other recognisers...
}
Note that setting your controller as delegate of the gestureRecognizer is important: this enables you to handle the following delegate method from the view controller (which was the main question):
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
// handle your logic, which gestureRecognizer should proceed...
return NO;
}
The handler method is the same is this example, but you can set up your own handlers as you like:
- (void)handleGesture:(UIGestureRecognizer*)gestureRecognizer {
// handle gesture (usually sorted by state), e.g.
// if(gesture.state == UIGestureRecognizerStateEnded) { ... }
}

Related

UINavigationController Custom Interactive Transition nested push animation can result in corrupted navigation bar

I am implementing a custom interactive navigation controller transition to "push" a new view controller by panning downwards on the navigation controller's view.
Everything works great, except in situations where (in efforts to break the app), I pan down again on the navigation controller's view to initiate a new interactive pushing of a view controller. If I do it quick enough after just finishing the previous one, I get a warning
"nested push animation can result in corrupted navigation bar"
And eventually as I continue to play with the app, I will get:
Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
I understand the for whatever reason, the context is not back in correct state, and there is something weird probably happening in the viewDidAppear/viewWillAppear methods in the pushed controller, but I'm not able to narrow down where exactly to stop the pushing of a new view controller before making sure the last view controller has FOR SURE finished being pushed.
I've played around with transition coordinator's notifyWhenInteractionEndsUsingBlock, by disabling the pan gesture until this block gets called, but this has not helped me.
I've searched on SO for these warnings, but it doesn't seem to apply to my situation - whereas I feel like its something to do with the context not being managed properly.
Here is the code I'm using:
I set a delegate to handle the pan gesture's begin state by pushing the view controller onto the stack:
Application.h
-(void)transitionManagerDidBeginDraggingDown:(TransitionManager *)transitionManager{
MenuViewController *menu = [[MenuViewController alloc] initWithNibName:#"MenuViewController" bundle:nil];
[self.navigationController pushViewController:menu animated:YES];
}
This method below is what initiates the delegate's pushing of the view controller onto the stack:
TransitionManage.h
- (void)panned:(UIPanGestureRecognizer*)gesture{
UIViewController *toVc = [self.context viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromVc = [self.context viewControllerForKey:UITransitionContextFromViewControllerKey];
switch (gesture.state) {
case UIGestureRecognizerStateBegan:{
if (self.interactiveTransitionUnderway == NO) {
self.interactive = YES;
CGPoint velocity = [gesture velocityInView:gesture.view];
if (velocity.y < 0) { // we are pulling upwards on the visible view
self.presenting = YES;
[self.delegate transitionManagerDidBeginDraggingUp:self];
}
else{ // we are pulling downwards on the visible view
self.presenting = NO;
[self.delegate transitionManagerDidBeginDraggingDown:self];
}
}
break;
}
case UIGestureRecognizerStateChanged:{
CGPoint touchLocation = [gesture locationInView:gesture.view];
CGPoint translation = [gesture translationInView:gesture.view];
CGPoint updatedCenter = CGPointMake(self.beginPoint.x, self.beginPoint.y + translation.y);
toVc.view.center = updatedCenter;
CGFloat d = fabs(touchLocation.y / CGRectGetHeight(self.parentViewController.view.bounds)) ;
[self.context updateInteractiveTransition:d];
break;
}
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateEnded:{
CGPoint velocity = [gesture velocityInView:gesture.view];
CGRect frame;
if (velocity.y < 0)
frame = CGRectMake(0, -toVc.view.frame.size.height, toVc.view.frame.size.width, toVc.view.frame.size.height); // pulling up
else
frame = CGRectMake(0, 0, toVc.view.frame.size.width, toVc.view.frame.size.height); // pulling down
[UIView animateWithDuration:0.75 delay:0.0 usingSpringWithDamping:0.8 initialSpringVelocity:0.0
options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionCurveEaseOut animations:^{
toVc.view.frame = frame;
} completion:^(BOOL finished) {
if (velocity.y < 0){
[self cancelInteractiveTransition];
}
else{
[self finishInteractiveTransition];
UIView *snapshotView = [fromVc.view snapshotViewAfterScreenUpdates:YES];
[toVc.view addSubview:snapshotView];
[toVc.view sendSubviewToBack:snapshotView];
}
[self completeTransition];
}];
break;
}
default:
break;
}
}
- (void)completeTransition{
BOOL finished = ![self.context transitionWasCancelled];
[self.context completeTransition:finished];
}
- (void)animationEnded:(BOOL)transitionCompleted {
// Reset to our default state
self.interactive = NO;
self.presenting = NO;
self.context = nil;
self.interactiveTransitionUnderway = NO;
}
I do not have a full answer to your issue but at least you should prevent your code from pushing a new viewcontroller with animation before the previous view controller is fully pushed (animation finished).

Disabling NSView fade animation for NSView `setHidden:`

I am working on a project that has the concept of draggable controls, everything is working fine except that NSView seems to employ a fade in/out animation when calling setHidden:.
I have been able to work around the problem by changing the line session.animatesToStartingPositionsOnCancelOrFail = YES; to NO and implementing the image snapback myself with a custom animated NSWindow subclass. it looks great, but I know there must be an easier way.
I have tried:
using NSAnimationContext grouping with duration of 0 around the setHidden: calls
setting the view animations dictionary using various keys (alpha, hidden, isHidden) on the control and superview
overriding animationForKey: for both the control and its superview
I am not using CALayers and have even tried explicitly setting wantsLayer: to NO.
Does anybody know how to either disable this animation, or have a simpler solution then my animated NSWindow?
here is my stripped down altered code with the bare minimum to see what I'm talking about.
#implementation NSControl (DragControl)
- (NSDraggingSession*)beginDraggingSessionWithDraggingCell:(NSActionCell <NSDraggingSource> *)cell event:(NSEvent*) theEvent
{
NSImage* image = [self imageForCell:cell];
NSDraggingItem* di = [[NSDraggingItem alloc] initWithPasteboardWriter:image];
NSRect dragFrame = [self frameForCell:cell];
dragFrame.size = image.size;
[di setDraggingFrame:dragFrame contents:image];
NSArray* items = [NSArray arrayWithObject:di];
[self setHidden:YES];
return [self beginDraggingSessionWithItems:items event:theEvent source:cell];
}
- (NSRect)frameForCell:(NSCell*)cell
{
// override in multi-cell cubclasses!
return self.bounds;
}
- (NSImage*)imageForCell:(NSCell*)cell
{
return [self imageForCell:cell highlighted:[cell isHighlighted]];
}
- (NSImage*)imageForCell:(NSCell*)cell highlighted:(BOOL) highlight
{
// override in multicell cubclasses to just get an image of the dragged cell.
// for any single cell control we can just make sure that cell is the controls cell
if (cell == self.cell || cell == nil) { // nil signifies entire control
// basically a bitmap of the control
// NOTE: the cell is irrelevant when dealing with a single cell control
BOOL isHighlighted = [cell isHighlighted];
[cell setHighlighted:highlight];
NSRect cellFrame = [self frameForCell:cell];
// We COULD just draw the cell, to an NSImage, but button cells draw their content
// in a special way that would complicate that implementation (ex text alignment).
// subclasses that have multiple cells may wish to override this to only draw the cell
NSBitmapImageRep* rep = [self bitmapImageRepForCachingDisplayInRect:cellFrame];
NSImage* image = [[NSImage alloc] initWithSize:rep.size];
[self cacheDisplayInRect:cellFrame toBitmapImageRep:rep];
[image addRepresentation:rep];
// reset the original cell state
[cell setHighlighted:isHighlighted];
return image;
}
// cell doesnt belong to this control!
return nil;
}
#pragma mark NSDraggingDestination
- (void)draggingEnded:(id < NSDraggingInfo >)sender
{
[self setHidden:NO];
}
#end
#implementation NSActionCell (DragCell)
- (void)setControlView:(NSView *)view
{
// this is a bit of a hack, but the easiest way to make the control dragging work.
// force the control to accept image drags.
// the control will forward us the drag destination events via our DragControl category
[view registerForDraggedTypes:[NSImage imagePasteboardTypes]];
[super setControlView:view];
}
- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp
{
BOOL result = NO;
NSPoint currentPoint = theEvent.locationInWindow;
BOOL done = NO;
BOOL trackContinously = [self startTrackingAt:currentPoint inView:controlView];
BOOL mouseIsUp = NO;
NSEvent *event = nil;
while (!done)
{
NSPoint lastPoint = currentPoint;
event = [NSApp nextEventMatchingMask:(NSLeftMouseUpMask|NSLeftMouseDraggedMask)
untilDate:[NSDate distantFuture]
inMode:NSEventTrackingRunLoopMode
dequeue:YES];
if (event)
{
currentPoint = event.locationInWindow;
// Send continueTracking.../stopTracking...
if (trackContinously)
{
if (![self continueTracking:lastPoint
at:currentPoint
inView:controlView])
{
done = YES;
[self stopTracking:lastPoint
at:currentPoint
inView:controlView
mouseIsUp:mouseIsUp];
}
if (self.isContinuous)
{
[NSApp sendAction:self.action
to:self.target
from:controlView];
}
}
mouseIsUp = (event.type == NSLeftMouseUp);
done = done || mouseIsUp;
if (untilMouseUp)
{
result = mouseIsUp;
} else {
// Check if the mouse left our cell rect
result = NSPointInRect([controlView
convertPoint:currentPoint
fromView:nil], cellFrame);
if (!result)
done = YES;
}
if (done && result && ![self isContinuous])
[NSApp sendAction:self.action
to:self.target
from:controlView];
else {
done = YES;
result = YES;
// this bit-o-magic executes on either a drag event or immidiately following timer expiration
// this initiates the control drag event using NSDragging protocols
NSControl* cv = (NSControl*)self.controlView;
NSDraggingSession* session = [cv beginDraggingSessionWithDraggingCell:self
event:theEvent];
// Note that you will get an ugly flash effect when the image returns if this is set to yes
// you can work around it by setting NO and faking the release by animating an NSWindowSubclass with the image as the content
// create the window in the drag ended method for NSDragOperationNone
// there is [probably a better and easier way around this behavior by playing with view animation properties.
session.animatesToStartingPositionsOnCancelOrFail = YES;
}
}
}
return result;
}
#pragma mark - NSDraggingSource Methods
- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
{
switch(context) {
case NSDraggingContextOutsideApplication:
return NSDragOperationNone;
break;
case NSDraggingContextWithinApplication:
default:
return NSDragOperationPrivate;
break;
}
}
- (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation
{
// now tell the control view the drag ended so it can do any cleanup it needs
// this is somewhat hackish
[self.controlView draggingEnded:nil];
}
#end
There must be a layer enabled somewhere in your view hierarchy, otherwise there wouldn't be a fade animation. Here is my way of disabling such animations:
#interface NoAnimationImageView : NSImageView
#end
#implementation NoAnimationImageView
+ (id)defaultAnimationForKey: (NSString *)key
{
return nil;
}
#end
The solution you already tried by setting the view animations dictionary should work. But not for the keys you mention but for the following. Use it somewhere before the animation is triggered the first time. If you have to do it on the window or view or both, I don't know.
NSMutableDictionary *animations = [NSMutableDictionary dictionaryWithDictionary:[[theViewOrTheWindow animator] animations];
[animations setObject:[NSNull null] forKey: NSAnimationTriggerOrderIn];
[animations setObject:[NSNull null] forKey: NSAnimationTriggerOrderOut];
[[theViewOrTheWindow animator] setAnimations:animations];
Or also just remove the keys if they are there (might not be the case as they are implicit / default):
NSMutableDictionary *animations = [NSMutableDictionary dictionaryWithDictionary:[[theViewOrTheWindow animator] animations];
[animations removeObjectForKey:NSAnimationTriggerOrderIn];
[animations removeObjectForKey:NSAnimationTriggerOrderOut];
[[theViewOrTheWindow animator] setAnimations:animations];
Ok. I figured out that the animation I'm seeing is not the control, the superview, nor the control's window. It appears that animatesToStartingPositionsOnCancelOrFail causes NSDraggingSession to create a window (observed with QuartzDebug) and put the drag image in it and it is this window that animates back to the origin and fades out before the setHidden: call is executed (i.e. before the drag operation is concluded).
Unfortunately, the window that it creates is not an NSWindow so creating a category on NSWindow doesn't disable the fade animation.
Secondly, there is no public way that I know of to get a handle on the window, so I can't attempt directly manipulating the window instance.
It looks like maybe my workaround is the best way to do this, after all its not far from what AppKit does for you anyway.
If anybody knows how to get a handle on this window, or what class it is I would be interested to know.

Moving UIImageViews in ScrollView

In my scrollview im having multiple imageviews where i can able to move each imageviews when user taps LONGPRESS i have done this using,
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(moveImage:)];
[panGesture setMaximumNumberOfTouches:2];
[panGesture setDelegate:self];
[imageview addGestureRecognizer:panGesture];
UILongPressGestureRecognizer *longPressRecognizer = [[UILongPressGestureRecognizer alloc] init];
[imageview addGestureRecognizer:longPressRecognizer];
- (BOOL) gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizerv {
if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
UIView *piece = gestureRecognizerv.view;
CGPoint locationInView = [gestureRecognizerv locationInView:piece];
CGPoint locationInSuperview = [gestureRecognizerv locationInView:piece.superview];
piece.layer.anchorPoint = CGPointMake(locationInView.x / piece.bounds.size.width, locationInView.y / piece.bounds.size.height);
piece.center = locationInSuperview;
}
}
- (void)moveImage:(UIPanGestureRecognizer *)gestureRecognizerg
{
NSLog(#"sdfgdsgsdg");
UIView *piece = [gestureRecognizerg view];
[self adjustAnchorPointForGestureRecognizer:gestureRecognizerg];
// need to test if the scrollview is already using the touches. If it is, leave them
if (!galleryView.tracking) {
if ([gestureRecognizerg state] == UIGestureRecognizerStateBegan || [gestureRecognizerg state] == UIGestureRecognizerStateChanged) {
CGPoint translation = [gestureRecognizerg translationInView:[piece superview]];
[piece setCenter:CGPointMake([piece center].x + translation.x, [piece center].y + translation.y)];
[gestureRecognizerg setTranslation:CGPointZero inView:[piece superview]];
}
}
}
Here my problem is the THE IMAGEVIEW is moving every where in scollview i need to rearrange the views i.e like Gallery view.
To get that rearrangement behavior you're going to need to have some logic that sets up a grid and 'snaps' elements to it, moves elements out the way when required, etc. It is reasonably complicated. Your current code simply moves views with your gestures.
However, if you are able to target iOS 6 and above you will almost certainly want to use a UICollectionView instead, which gives you a lot of this for free. Collection views simplify the layout and logic behind re-arranging, and will generally make your life a lot easier.

detect long press on UINavigationItem's back button

I want to add functionality to my back buttons through my UINavigationController-based app where long-pressing the back button will pop to root. However, I can't figure out where to attach the gesture recognizer. Do I subclass UINavigationBar and try and detect if the long press is in the left button region?
I've heard of people adding similar functionality before. Anyone have any ideas?
I know this question is old, but I came up with a solution. Instead of trying to add the gesture recognizer to the button itself (which would be ideal), I added it to the self.navigationController.navigationBar and then in the action method, use the locationInView to see if I'm over the back button. I wasn't entirely sure about how to identify the back button precisely, so I'm clumsily just grabbing the the first subview with an x coordinate less than some arbitrary value, but it seems promising. If someone has a better way to identify the frame of the back button, let me know.
- (void)longPress:(UILongPressGestureRecognizer *)sender
{
if (sender.state == UIGestureRecognizerStateEnded)
{
// set a default rectangle in case we don't find the back button for some reason
CGRect rect = CGRectMake(0, 0, 100, 40);
// iterate through the subviews looking for something that looks like it might be the right location to be the back button
for (UIView *subview in self.navigationController.navigationBar.subviews)
{
if (subview.frame.origin.x < 30)
{
rect = subview.frame;
break;
}
}
// ok, let's get the point of the long press
CGPoint longPressPoint = [sender locationInView:self.navigationController.navigationBar];
// if the long press point in the rectangle then do whatever
if (CGRectContainsPoint(rect, longPressPoint))
[self doWhatever];
}
}
- (void)addLongPressGesture
{
if (NSClassFromString(#"UILongPressGestureRecognizer"))
{
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPress:)];
[self.navigationController.navigationBar addGestureRecognizer:longPress];
[longPress release];
}
}
I believe UIGestureRecognizers can only be added to UIViews and subclasses of UIViews.
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIView_Class/UIView/UIView.html
The back button is a UIBarButtonItem that descends from NSObject. Therefore, you won't be able to attach a gesture recognizer to a standard back button using
UILongPressGestureRecognizer *longPressGesture =
[[[UILongPressGestureRecognizer alloc]
initWithTarget:self action:#selector(longPress:)] autorelease];
[self.navigationItem.backBarButtonItem addGestureRecognizer:longPressGesture];
You can however add a custom view to a UIBarButtonItem. A custom view could just as easily be a UIView, UIButton, UILabel, etc.
Example:
UIView *myTransparentGestureView = [[UIView alloc] initWithFrame:CGRectMake(0,0,40,30)];
[myTransparentGestureView addGestureRecognizer:longPressGesture];
[self.navigationItem.backBarButtonItem setCustomView:myTransparentGestureView];
// Or you could set it like this
// self.navigationItem.backBarButtonItem.customView = myTransparentGestureView;
[myTransparentGestureView release];
You have to be careful however, since setting properties on backBarButtonItem applies to the next view that you push. So if you have view A that pushes to view B and you want the gesture to be recognized when you tap back in view B. You must set it up in view A.
I followed a slightly different path, figured I'd share it. The above answers are fine, but really, if the long press is in the leading 1/3 of the nav bar, that's good enough for me:
- (void)longPress:(UILongPressGestureRecognizer *)gr
{
NSLog(#"longPress:");
UINavigationBar *navBar = [self navigationBar];
CGFloat height = navBar.bounds.size.height;
CGPoint pt = [gr locationOfTouch:0 inView:navBar];
//NSLog(#"PT=%# height=%f", NSStringFromCGPoint(pt), height);
if(CGRectContainsPoint(CGRectMake(0,0,100,height), pt)) {
[self popToViewController:self.viewControllers[0] animated:YES];
}
}
Here's my solution:
In appDelegate (the "owner" of the nav bar in my app), In applicationDidFinishLaunchingWithOptions:
Get the nav bar view and add the gesture recognizer to the whole view:
// Get the nav bar view
UINavigationBar *myNavBar = nil;
for (UIView *view in [self.window.rootViewController.view subviews]) {
if ([view isKindOfClass:[UINavigationBar class]]) {
NSLog(#"Found Nav Bar!!!");
myNavBar = (UINavigationBar *)view;
}
}
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self
action:#selector(backButtonLongPress:)];
[myNavBar addGestureRecognizer:longPress];
NSLog(#"Gesture Recognizer Added.");
Then in appDelegate, in -(void) backButtonLongPress:(id) sender
Check to see if the gesture occurs within the frame of the back button:
if ([sender state] == UIGestureRecognizerStateBegan) {
// Get the nav bar view
UINavigationBar *myNavBar = nil;
for (UIView *view in [self.window.rootViewController.view subviews]) {
if ([view isKindOfClass:[UINavigationBar class]]) {
NSLog(#"Found Nav Bar!!!");
myNavBar = (UINavigationBar *)view;
}
}
// Get the back button view
UIView *backButtonView = nil;
for (UIView *view in [myNavBar subviews]) {
if ([[[view class] description] isEqualToString:#"UINavigationItemButtonView"]) {
backButtonView = view;
NSLog(#"Found It: %#", backButtonView);
NSLog(#"Back Button View Frame: %f, %f; %f, %f", backButtonView.frame.origin.x, backButtonView.frame.origin.y, backButtonView.frame.size.width, backButtonView.frame.size.height);
}
}
CGPoint longPressPoint = [sender locationInView:myNavBar];
NSLog(#"Touch is in back button: %#", CGRectContainsPoint(backButtonView.frame, longPressPoint) ? #"YES" : #"NO");
if (CGRectContainsPoint(backButtonView.frame, longPressPoint)) {
// Place your action here
}
// Do nothing if outside the back button frame
}

iPhone OS3 changes to UIScrollView subclasses

I have a subclass of UIScrollView that overrides
touchesBegan:withEvent:
touchesMoved:withEvent:
touchesEnded:withEvent:
Overriding these three seems to be a technique that is widely used (based on my observations in forums). However, as soon as I compiled this code on OS3, these methods are no longer being called. Has anyone else seen this problem? Is there a known fix that doesn't use undocumented methods?
My first attempt at a solution was to move all the touchesBegan/Moved/Ended methods down into my content view and set
delaysContentTouches = NO;
canCancelContentTouches = NO;
This worked partially, but left me unable to pan when I have zoomed. My second attempt only set canCancelContentTouches = NO when there were two touches (thus passing the pinch gesture through to the content). This method was sketchy and didn't work very well.
Any ideas? My requirement is that the scroll view must handle the pan touches, and I must handle the zoom touches.
My solution is not pretty. Basically there is a scroll view who contains a content view. The scroll view does not implement touchesBegan,Moved,Ended at all. The content view maintains a pointer to his parent (called "parentScrollView" in this example). The content view handles the logic and uses [parentScrollView setCanCancelContentTouches:...] to determine whether or not to let the parent view cancel a touch event (and thus perform a scroll event). The tap count logic is there because users rarely place both fingers onscreen at exactly the same time so the first touch must be ignored if it is very quickly followed by a second.
-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
if(parentViewIsUIScrollView)
{
UIScrollView * parentScrollView = (UIScrollView*)self.superview;
if([touches count] == 1)
{
if([[touches anyObject] tapCount] == 1)
{
if(numberOfTouches > 0)
{
[parentScrollView setCanCancelContentTouches:NO];
//NSLog(#"cancel NO - touchesBegan - second touch");
numberOfTouches = 2;
}
else
{
[parentScrollView setCanCancelContentTouches:YES];
//NSLog(#"cancel YES - touchesBegan - first touch");
numberOfTouches = 1;
}
}
else
{
numberOfTouches = 1;
[parentScrollView setCanCancelContentTouches:NO];
//NSLog(#"cancel NO - touchesBegan - doubletap");
}
}
else
{
[parentScrollView setCanCancelContentTouches:NO];
//NSLog(#"cancel NO - touchesBegan");
numberOfTouches = 2;
}
//NSLog(#"numberOfTouches_touchesBegan = %i",numberOfTouches);
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if(touchesCrossed)
return;
if(parentViewIsUIScrollView)
{
UIScrollView * parentScrollView = (UIScrollView*)self.superview;
NSArray * thoseTouches = [[event touchesForView:self] allObjects];
if([thoseTouches count] != 2)
return;
numberOfTouches = 2;
/* compute and perform pinch event */
[self setNeedsDisplay];
[parentScrollView setContentSize:self.frame.size];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
touchesCrossed = NO;
if(parentViewIsUIScrollView)
{
numberOfTouches = MAX(numberOfTouches-[touches count],0);
[(UIScrollView*)self.superview setCanCancelContentTouches:YES];
//NSLog(#"cancel YES - touchesEnded");
//NSLog(#"numberOfTouches_touchesEnded = %i",numberOfTouches);
}
}