http://stackoverflow.com/questions/2003201/observing-pinch-multi-touch-gestures-in-a-uitableview
I am looking to implement a pinch in/out on top of a UITableView, I have looked at several methods including this one:
But while I can create a UIViewTouch
object and overlay it onto my UITableView, scroll events are not being relayed to my UITableView, I can still select cells, and they respond properly by triggering a transition to a new ViewController object. But I can not scroll the UITableView despite passing the touchesBegan, touchesMoved, and touchesEnded events.
answer:
This seems to be a classic problem. In my case I wanted to intercept some events over a UIWebView which can't be subclassed, etc etc.
I've found that the best way to do it is to intercept the events using the UIWindow:
EventInterceptWindow.h
@protocol EventInterceptWindowDelegate - (BOOL)interceptEvent:(UIEvent *)event; // return YES if event handled @end @interface EventInterceptWindow : UIWindow { // It would appear that using the variable name 'delegate' in any UI Kit // subclass is a really bad idea because it can occlude the same name in a // superclass and silently break things like autorotation. id <EventInterceptWindowDelegate> eventInterceptDelegate; } @property(nonatomic, assign) id <EventInterceptWindowDelegate> eventInterceptDelegate; @end
EventInterceptWindow.m:
#import "EventInterceptWindow.h" @implementation EventInterceptWindow @synthesize eventInterceptDelegate; - (void)sendEvent:(UIEvent *)event { if ([eventInterceptDelegate interceptEvent:event] == NO) [super sendEvent:event]; } @end
Create that class, change the class of your UIWindow in your MainWindow.xib to EventInterceptWindow, then somewhere set the eventInterceptDelegate to a view controller that you want to intercept events. Example that intercepts a double-tap:
- (BOOL)interceptEvent:(UIEvent *)event { NSSet *touches = [event allTouches]; UITouch *oneTouch = [touches anyObject]; UIView *touchView = [oneTouch view]; // NSLog(@"tap count = %d", [oneTouch tapCount]); // check for taps on the web view which really end up being dispatched to // a scroll view if (touchView && [touchView isDescendantOfView:webView] && touches && oneTouch.phase == UITouchPhaseBegan) { if ([oneTouch tapCount] == 2) { [self toggleScreenDecorations]; return YES; } } return NO; }
Related info here: http://iphoneincubator.com/blog/windows-views/360idev-iphone-developers-conference-presentation
another:
#import <UIKit/UIKit.h> #import "EventInterceptWindow.h" @interface ViewController : UIViewController<EventInterceptWindowDelegate> { int initialDistance; } - (BOOL) interceptEvent:(UIEvent *) event; @end
- (void) viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // Register to receive touch events AppDelegate *appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate]; EventInterceptWindow *window = (EventInterceptWindow *) appDelegate.window; window.eventInterceptDelegate = self; } - (void) viewWillDisappear:(BOOL) animated { // Deregister from receiving touch events AppDelegate *appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate]; EventInterceptWindow *window = (EventInterceptWindow *) appDelegate.window; window.eventInterceptDelegate = nil; [super viewWillDisappear:animated]; } - (BOOL) interceptEvent:(UIEvent *) event { NSSet *touches = [event allTouches]; // Give up if user wasn't using two fingers if([touches count] != 2) return NO; UITouchPhase phase = ((UITouch *) [touches anyObject]).phase; CGPoint firstPoint = [[[touches allObjects] objectAtIndex:0] locationInView:self.view]; CGPoint secondPoint = [[[touches allObjects] objectAtIndex:1] locationInView:self.view]; CGFloat deltaX = secondPoint.x - firstPoint.x; CGFloat deltaY = secondPoint.y - firstPoint.y; CGFloat distance = sqrt(deltaX*deltaX + deltaY*deltaY); if(phase == UITouchPhaseBegan) { initialDistance = distance; } else if(phase == UITouchPhaseMoved) { CGFloat currentDistance = distance; if(initialDistance == 0) initialDistance = currentDistance; else if(currentDistance - initialDistance > kMinimumPinchDelta) NSLog(@"Zoom in"); else if(initialDistance - currentDistance > kMinimumPinchDelta) NSLog(@"Zoom out"); } else if(phase == UITouchPhaseEnded) { initialDistance = 0; } return YES; }
#import <UIKit/UIKit.h> #import "EventInterceptWindow.h" @class ViewController; @interface AppDelegate : UIResponder <UIApplicationDelegate> { EventInterceptWindow *_window; ViewController *_viewController; } @property (strong, nonatomic) EventInterceptWindow *window; @property (strong, nonatomic) ViewController *viewController; @end
#import "AppDelegate.h" #import "ViewController.h" @implementation AppDelegate @synthesize window = _window; @synthesize viewController = _viewController; - (void)dealloc { [_window release]; [super dealloc]; } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { EventInterceptWindow *winTemp = [[EventInterceptWindow alloc]initWithFrame:[[UIScreen mainScreen]bounds]]; self.window = winTemp; [winTemp release]; ViewController *temp = [[ViewController alloc]initWithNibName:@"ViewController" bundle:nil]; self.viewController = temp; [temp release]; self.window.rootViewController = _viewController; [self.window makeKeyAndVisible]; return YES; }