I have two .m files. The first is the main code, The second is a subclass of UIImageView so that i can detect touches.
In the main .m file I have added a progress bar and a customimageview both subviews of a scrollview.
What I need is that when a user touches the customimageview that the progress bar moves up and a double tap decreases the [Note: the customimageview has to have its touches recognised in the second .m because of them being in a subview of a scrollview and other controls are having to be handled]
In the main .m file I have a two methods:
- (void)pumpsingletap {
progbm.progress +=0.1;
}
- (void)pumpdoubletap {
progbm.progress -=0.1;
}
then in the subclassed uiimageview i have:
//inside touches method
if ([touch view].tag == 555) {
NSLog(#"pump touched");
switch ([allTouches count]) {
case 1: {
switch ([touch tapCount]) {
//---single tap---
case 1: {
NSLog(#"single pump touch");
[self performSelector:#selector(pumpsingletap) withObject:nil afterDelay:.4];
} break;
//---double tap---
case 2: {
NSLog(#"double pump touch");
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(pumpsingletap) object:nil];
[self performSelector:#selector(pumpdoubletap) withObject:nil afterDelay:.4];
} break;
}
}
}
}
So the NSlog's appear so the touch recognition isn't an issue. But the performSelector falls over. As the customimageview pumpsingletap doesnt work.
So how do i call the method in the subclass.
//update//
so I have added in the following code, in my subclass
mainMethod* callingMethod = [[mainMethod alloc] init];
[callingMethod performSelector:#selector(pumpsingletap) withObject:nil afterDelay:.4];
then in my main method for pumpsingletap i changed it to:
- (void)pumpsingletap {
NSLog(#"single pump method called");
progbm.progress +=0.1;
}
The NSLog for single pump method called appeared but the progress bar progbm - didn't move. so i have solved my calling issue - just need to now work out why the progress bar isnt moving!!
If I'm following the code correctly, you are performing the selector on self, but self is your derived UIImageView class (so you probably crash at that point?). You need to perform selector on the class in your main file. Pass a reference to that to your derived class. Alternately, you could create a delegate, implement it in your main class, pass your main class to the UIImageView and then call through the delegate (there are even other ways to do it (key-value observation), but one of these should work).
I'm not sure if this is the problem, but you don't need brackets around case statements. Also I don't get why you would make a switch within a switch. Just make an if-elseif-else statement, it'll probably be easier to understand.
Other than this, from what I understand, you have a view controller with both a progress bar and a customimageview as properties, and you have methods that should be called in response to certain actions, (tapping or double tapping the customimageview) but they're in the view controller. The usual way to solve this is by using the target action mechanism. UIControls implement the target action mechanism by encapsulating target-action pairs and storing them in a dictionary, keyed by the event type (UIControlEvent). Here's a slightly simpler version.
In the .h file for your subclass of UIImageView, before the #interface write this:
typedef enum {
ControlEventTap = 0,
ControlEventDoubleTap
} ControlEvent;
Then in the .m file add this before the #implementation:
#interface TargetActionPair : NSObject {
id target;
SEL action;
}
#property (nonatomic, assign) id target;
#property (nonatomic, assign) SEL action;
#end
#implementation TargetActionPair
#synthesize target, action;
#end
Then, add an NSMutableArray instance variable, (but not a property) and a - (void)setTarget:(id)t action:(SEL)a forEvent:(ControlEvent)e method to your customimageview implementation.
The method should look like this:
- (void)setTarget:(id)t action:(SEL)a forEvent:(ControlEvent)e {
TargetActionPair *tar_act = [[TargetActionPair alloc] init];
tar_act.target = t;
tar_act.action = a;
// actionsArray is the mutable array instance variable and must be allocated and set in the init method for customimageview.
[actionsArray replaceObjectAtIndex:(NSUInteger)e withObject:tar_act];
[tar_act release];
}
Then you can replace your touch handling code with:
if ([touch view].tag == 555) {
NSUInteger tapcount = [touch tapCount];
if (([alltouches count] == 1) && (tapcount <= [actionsArray count])) {
TargetActionPair *tar_act = [actionsArray objectAtIndex:tapcount-1];
[tar_act.target performSelector:tar_act.action withObject:nil afterDelay:.4];
if (tapcount == 2) {
TargetActionPair *tar_act2 = [actionsArray objectAtIndex:tapcount-2];
[NSObject cancelPreviousPerformRequestsWithTarget:tar_act2.target selector:tar_act2.action object:nil];
}
}
}
With this code you simply set the target and action for each control event in the viewDidLoad method of the view controller that contains the customimageview. So the calls would look like this:
[self.customimageview setTarget:self action:#selector(pumpsingletap) forEvent:ControlEventTap];
[self.customimageview setTarget:self action:#selector(pumpdoubletap) forEvent:ControlEventDoubleTap];
DON'T FORGET to release the actionsArray in your dealloc method and to be very careful about releasing the view controller since the customimageview doesn't retain it.
I hope this helps, best of luck on your app.
In the end I solved the issue by using NSNotificationCenter
Related
So, I want to place a label in my fist view and place in a second one a UISwitch.
But the problem is I can't link together everything.. :/
in my first view i have that
- (void)onRoff {
if (mySwitch1.on) {
test.hidden = YES;
}
else (test.hidden = NO);
}
but here I have an error with mySwitch1 because it's declared in my secondView..
I don't know if it's clear, I want to link a label and a switch in different view..
Thanks !
Indeed you are not very clear. The first thing you might want to try is describe what you did:
how are your two views instantiated?
Let's assume your two views are instantiated from two different nib files.
what is the object you want to have access to your label and switch?
Let's assume it's a view controller. It's a bit unusual for a single view controller to control two views from two different nib files, but after all, why not?
In any case, you can set the owner class for your two nib files to be the class of your view controller. Then in Interface Builder, from the first view, you can bind the label to the file owner's UILabel outlet. And in Interface Builder, from the second view, you can bind the UISwitch to the file owner's second outlet, of type UISwitch.
But perhaps the onRoff methods of yours is actually a method of one of your two view class? The same idea apply: you can set the file owner in the second nib file to be the view class of the first view, and then bind the switch to the file owner's UISwitch outlet.
But it sounds like your design might be worth working on...
Edit: after your comment, here is a bit more...
The problem is that your two view controllers each control a different page and have no reason to know about each other. So you need a middle man object. That could be another controller. Let's use the Application delegate. Then, in the IBAction method of your SwitchViewController, you can do something like:
- (IBAction) switchChangedValue:(UISwitch *) sender {
NSString *newLabelText = sender.isOn ? #"On" : #"Off";
self.labelViewController.label.text = newLabelText;
}
Now how will everybody know about each other? First each view controller will inform the middle man. Here is it for the SwitchViewController:
- (void) viewDidLoad
{
[super viewDidLoad];
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.switchViewController = self;
}
Second, the app delegate will need to coordinate everything:
#interface MyAppDelegate : …
#property (nonatomic, retain) SwitchViewController *switchViewController;
#property (nonatomic, retain) LabelViewController *labelViewController;
#end
#implementation MyAppDelegate
#synthesize switchViewController = _switchViewController;
#synthesize labelViewController = _labelViewController;
- (void) setSwitchViewController:(SwitchViewController *) newSwitchController {
if (newSwitchController != _switchViewController) {
[_switchViewController release];
_switchViewController = [newSwitchController retain];
_switchViewController.labelViewController = _labelViewController;
if (_labelViewController)
_labelViewController.label.text = _switchViewController.switch.isOn ? #"On" : #"Off";
}
}
- (void) setLabelViewController:(LabelViewController *) newLabelController {
if (newLabelController != _labelViewController) {
[_labelViewController release];
_labelViewController = [newLabelController retain];
_labelViewController.switchViewController = _switchViewController;
if (_switchViewController)
_labelViewController.label.text = _switchViewController.switch.isOn ? #"On" : #"Off";
}
}
I left out a number of details, but I hope the big picture is clear.
So you have declared ur UISwitch in the second view and ur label in the first view. All u have to do is just use NSUserDefaults to achieve wat u want. Have the following method in the second view itself. Dont bring it to the first view.
- (void)onRoff {
if (mySwitch1.on) {
[[NSUserDefaults standarduserdefaults]setObject:#"off" forKey:#"state"];
[[NSUserDefaults standarduserdefaults]synchronize];
}
else {
[[NSUserDefaults standarduserdefaults]setObject:#"on" forKey:#"state"];
[[NSUserDefaults standarduserdefaults]synchronize];
}
}
Now in the viewWillAppear method of the first view just chk the value of the NSUserDefaults..
-(void)chkState{
NSString *tempStr=[[NSUserDefaults standarduserdefaults]objectForKey:#"state"];
if([tempStr isEqualTo:#"on"]) {
test.hidden=YES;
}
else {
test.hidden=NO;
}
}
Call this method in the viewWillAppear of the firstview like this....
[self chkState];
Hope this helps....If u want save the state of the switch too then just chk the userdefaults value again in the viewWilAppear method of the 2nd view and based
I work on a project for iPhone iOS 4 with Xcode 4.
I have subclassed a UIButton so that it intercepts single and double tap.
This is the final part of #implementation of the UIButton subclass, two instance methods where the taps are "recorded";
- (void) handleTap: (UITapGestureRecognizer *) sender {
NSLog(#"single tap");
}
- (void) handleDoubleTap :(UITapGestureRecognizer *) sender {
NSLog(#"double tap");
}
A button instance is created in nib and all works OK: it intercepts single tap and double tap and output the NSLog.
Now the question: I have in my ViewController two methods (resetAllFields and populateAllFields) and I need that single tap execute resetAllFields and double tap execute populateAllFields.
How can I do? Where do I put the call?
Thank you.
If you want to handle the behavior in the ViewController the typical solution is to add a #protocol in your custom button class that defines methods for handling the single and double taps.
i.e. in your CustomButton.h
#protocol CustomButtonDelegate <NSObject>
- (void)button:(CustomButton *)button tappedWithCount:(int)count;
#end
You then have a delegate that implements this protocol in your custom button class and call those methods on the delegate when your taps are detected.
i.e. in your CustomButton.h
id <CustomButtonDelegate> _delegate;
in your methods in the implementation:
- (void) handleTap: (UITapGestureRecognizer *) sender {
NSLog(#"single tap");
[self.delegate button:self tappedWithCount:1];
}
- (void) handleDoubleTap :(UITapGestureRecognizer *) sender {
NSLog(#"double tap");
[self.delegate button:self tappedWithCount:2];
}
Your View Controller than implements the protocol methods and sets itself as the custom button's delegate.
ie. in your ViewControllers implementation
- (void)button:(CustomButton *)button tappedWithCount:(int)count {
if (count == 1) {
[self resetAllFields];
} else if (count == 2) {
[self populateAllFields];
}
}
Since you are using Interface Builder to set the custom button you can assign your view controller as a delegate in there or in the ViewDidLoad.
I have a class that instances some other classes. It's working to control their lifespan, or supposed to ;) A root class managing others which contain events and are subviews of the root view.
I have a lot of graphics involved per view and need to clear things out before loading the next.
Any idea how to unload the current subview and how to load the next subview, while maintaining a named reference to it to use in event handling, from a "touches ended" method?
Thanks
// :)
Assuming that you want to dump the old subview and hold a handle to the new one, this is actually pretty easy. You want something like this:
#interface YourView : UIView
{
// Create an ivar in your class
UIView *_subview;
}
// Propertize it as retain to take care of most of the heavy lifting
#property(nonatomic, retain) UIView *subview;
#end
#implementation YourView
// Map the ivar to the property
#synthesize subview = _subview;
// Call this to put in a new subview
-(void) switchToNewSubview:(UIView*)newSubview
{
// Remove the old subview, set the new one, and if the new one isn't nil
// add it as a subview
[self.subview removeFromSuperview];
self.subview = newSubview;
if(newSubview)
[self addSubview:self.subview];
}
// Don't forget to nil out the subview on dealloc to release it
-(void) dealloc
{
self.subview = nil;
[super dealloc];
}
#end
I'm going to write my own custom control that is very different from UIButton. It's so much different that I decided to write it from scratch. So all I subclass is UIControl.
When my control is touched up inside, then I want to fire a message in means of target-action. The user of that class may instantiate it and then add some targets and actions for this event.
i.e. imagine I would call internally a method -fireTargetsForTouchUpEvent. How could I maintain this target-action-mechanism in my class? Do I have to add all targets and actions to my own array and then just call selectors (the actions) on the target objects in a for-loop? Or is there a more intelligent way to do it?
I imagine to provide some methods for adding targets and actions for some events like that touch up event (I raise that manually by calling a internal method when that happens). Any idea?
I just want to clarify what #Felixyz said because it wasn't clear to me at first.
If you are subclassing UIControl, even if you are going to have a custom event, you don't have to keep track of your own targets/actions. The functionality is already there, all you have to do is call the code below in your subclass to trigger the event:
[self sendActionsForControlEvents:UIControlEventValueChanged];
Then in the view or view controller that instantiates your custom UIControl, just do
[customControl addTarget:self action:#selector(whatever) forControlEvents:UIControlEventValueChanged];
For custom event, just define your own enum (for example, UIControlEventValueChanged is equal to 1 << 12). Just make sure it is within the permitted range defined by UIControlEventApplicationReserved
You have the right idea. Here is how I would do it:
#interface TargetActionPair : NSObject
{
id target;
SEL action;
}
#property (assign) id target;
#property (assign) SEL action;
+ (TargetActionPair *)pairWithTarget:(id)aTarget andAction:(SEL)selector;
- (void)fire;
#end
#implementation TargetActionPair
#synthesize target;
#synthesize action;
+ (TargetActionPair *)pairWithTarget:(id)aTarget andAction:(SEL)anAction
{
TargetActionPair * newSelf = [[self alloc] init];
[newSelf setTarget:aTarget];
[newSelf setAction:anAction];
return [newSelf autorelease];
}
- (void)fire
{
[target performSelector:action];
}
#end
With that class in place, storing your target/action pairs is pretty straightforward:
MyCustomControl.h:
#import "TargetActionPair.h"
#interface MyCustomControl : UIControl
{
NSMutableArray * touchUpEventHandlers;
}
- (id)init;
- (void)dealloc;
- (void)addHandlerForTouchUp:(TargetActionPair *)handler;
#end
MyCustomControl.m:
#import "TargetActionPair.h"
#implementation MyCustomControl
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
touchUpEventHandlers = [[NSMutableArray alloc] initWithCapacity:0];
return self;
}
- (void)dealloc
{
[touchUpEventHandlers release];
}
- (void)addHandlerForTouchUp:(TargetActionPair *)handler
{
[touchUpEventHandlers addObject:handler];
}
- (void) fireTargetsForTouchUpEvent
{
[touchUpEventHandlers makeObjectsPerformSelector:#selector(fire)];
}
#end
After that, setting up the control would be done as follows:
[instanceOfMyControl addHandlerForTouchUp:
[TargetActionPair pairWithTarget:someController
andAction:#selector(touchUpEvent)];
Since you're planning to subclass UIControl, you can just use
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents;
Using this, any class can register itself as a target for any events it wants to on your custom controller.
I have a UIWebView with a navigation bar and toolbar that I want to auto hide. I'm doing that already, but I want to show them when the user taps on the UIWebView.
My problem is that the UIWebView captures all the touches events, and I cannot intercept the one I need.
Is there any workaround for this?
Taken from a blog entry (doesn't load, but thankfully it's Google Cache'd) by Mithin Kumar.
(Mithin, I hope you don't mind me reposting it here; if you do, please let me know in the comments and I will remove it.)
Recently, I was working on a project which required detection of tap and events on the UIWebView. We wanted to find out the HTML element on which the user taps in the UIWebView and then depending on the element tapped some action was to be performed. After some Googling, I found out the most of the users lay a transparent UIView on top of the UIWebView, re-implement the touch methods of UIResponder class (Ex: -touchesBegan:withEvent:) and then pass the events to the UIWebView. This method is explained in detail here. There are multiple problems with the method.
Copy/Selection stops working on UIWebView
We need to create a sub-class of UIWebView while Apple says we should not sub-class it.
A lot other UIWebView features stop working.
We ultimately found out that the right way to implement this is by
sub-classing UIWindow and re-implementing the -sendEvent: method. Here
is how you can do it. First, create a UIWindow sub-class
#import <UIKit/UIKit.h>
#protocol TapDetectingWindowDelegate
- (void)userDidTapWebView:(id)tapPoint;
#end
#interface TapDetectingWindow : UIWindow {
UIView *viewToObserve;
id <TapDetectingWindowDelegate> controllerThatObserves;
}
#property (nonatomic, retain) UIView *viewToObserve;
#property (nonatomic, assign) id <TapDetectingWindowDelegate> controllerThatObserves;
#end
Note that we have variables which tell us the UIView on which to
detect the events and the controller that receives the event
information. Now, implement this class in the following way
#import "TapDetectingWindow.h"
#implementation TapDetectingWindow
#synthesize viewToObserve;
#synthesize controllerThatObserves;
- (id)initWithViewToObserver:(UIView *)view andDelegate:(id)delegate {
if(self == [super init]) {
self.viewToObserve = view;
self.controllerThatObserves = delegate;
}
return self;
}
- (void)dealloc {
[viewToObserve release];
[super dealloc];
}
- (void)forwardTap:(id)touch {
[controllerThatObserves userDidTapWebView:touch];
}
- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];
if (viewToObserve == nil || controllerThatObserves == nil)
return;
NSSet *touches = [event allTouches];
if (touches.count != 1)
return;
UITouch *touch = touches.anyObject;
if (touch.phase != UITouchPhaseEnded)
return;
if ([touch.view isDescendantOfView:viewToObserve] == NO)
return;
CGPoint tapPoint = [touch locationInView:viewToObserve];
NSLog(#"TapPoint = %f, %f", tapPoint.x, tapPoint.y);
NSArray *pointArray = [NSArray arrayWithObjects:[NSString stringWithFormat:#"%f", tapPoint.x],
[NSString stringWithFormat:#"%f", tapPoint.y], nil];
if (touch.tapCount == 1) {
[self performSelector:#selector(forwardTap :) withObject:pointArray afterDelay:0.5];
}
else if (touch.tapCount > 1) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(forwardTap :) object:pointArray];
}
}
#end
Implement the sendEvent method in the above way, and then you can send
back the information you want back to the controller.
There are few things that one needs to keep in mind. Make sure in your
MainWindow.xib file, the window is of type TapDetectingWindow and not
UIWindow. Only then all the events will pass through the above
re-implemented sendEvent method. Also, make sure you call [super
sendEvent:event] first and then do whatever you want.
Now, you can create your UIWebView in the controller class in the
following way
#interface WebViewController : UIViewController<TapDetectingWindowDelegate> {
IBOutlet UIWebView *mHtmlViewer;
TapDetectingWindow *mWindow;
}
- (void)viewDidLoad {
[super viewDidLoad];
mWindow = (TapDetectingWindow *)[[UIApplication sharedApplication].windows objectAtIndex:0];
mWindow.viewToObserve = mHtmlViewer;
mWindow.controllerThatObserves = self;
}
Remember you’ll need to write the method userDidTapWebView in your
controller class. This is the method that is called in order to send
the event information to the controller class. In our case above we
are sending the point in the UIWebView at which the user tapped.
You can very simply use a UITapGestureRecognizer to detect tap gestures on a UIWebView. You must implement a UIGestureRecognizerDelegate method to allow the simultaneous recognition however.
- (void)viewDidLoad{
[super viewDidLoad];
UITapGestureRecognizer *targetGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
targetGesture.numberOfTapsRequired = 2;
targetGesture.delegate = self;
[self.webView addGestureRecognizer:targetGesture];
}
// called when the recognition of one of gestureRecognizer or otherGestureRecognizer would be blocked by the other
// return YES to allow both to recognize simultaneously. the default implementation returns NO (by default no two gestures can be recognized simultaneously)
//
// note: returning YES is guaranteed to allow simultaneous recognition. returning NO is not guaranteed to prevent simultaneous recognition, as the other gesture's delegate may return YES
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
NSLog(#"%#", otherGestureRecognizer);
//if you would like to manipulate the otherGestureRecognizer here is an example of how to cancel and disable it
if([otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]){
UITapGestureRecognizer *tapRecognizer = (UITapGestureRecognizer*)otherGestureRecognizer;
if(tapRecognizer.numberOfTapsRequired == 2 && tapRecognizer.numberOfTouchesRequired == 1){
//this disalbes and cancels all other singleTouchDoubleTap recognizers
// default is YES. disabled gesture recognizers will not receive touches. when changed to NO the gesture recognizer will be cancelled if it's currently recognizing a gesture
otherGestureRecognizer.enabled = NO;
}
}
return YES;
}
-(void)handleTap:(id)sender{
}
I had several issues trying to use brian.clear's answer (copied from a extinct post by Mithin Kumar) in my iOS 5-7, universal, storyboard-based, totally ARC project so I had to make several changes. I also improved some things and made it easier to understand (at least for me). If you are having trouble trying to use that answer from 2009 maybe you should try my update. Detailed instructions:
1. Create a new TapDetectingWindow class
TapDetectingWindow.h
// Created by Cristian Perez <cpr#cpr.name>
// Based on https://stackoverflow.com/a/1859883/423171
#import <UIKit/UIKit.h>
#protocol TapDetectingWindowDelegate
- (void)userDidTapView:(CGPoint)tapPoint;
#end
#interface TapDetectingWindow : UIWindow
#property (nonatomic) UIView *tapDetectingView;
#property (nonatomic) id <TapDetectingWindowDelegate> tapDetectedDelegate;
#end
TapDetectingWindow.m
// Created by Cristian Perez <cpr#cpr.name>
// Based on https://stackoverflow.com/a/1859883/423171
#import "TapDetectingWindow.h"
#implementation TapDetectingWindow
#synthesize tapDetectingView;
#synthesize tapDetectedDelegate;
- (id)initWithFrame:(CGRect)frame
{
return [super initWithFrame:frame];
}
- (void)sendEvent:(UIEvent *)event
{
[super sendEvent:event];
if (tapDetectingView == nil || tapDetectedDelegate == nil)
{
return;
}
NSSet *touches = [event allTouches];
if (touches.count != 1)
{
return;
}
UITouch *touch = touches.anyObject;
if (touch.phase != UITouchPhaseEnded)
{
return;
}
if (touch.view != nil && ![touch.view isDescendantOfView:tapDetectingView])
{
return;
}
CGPoint tapPoint = [touch locationInView:tapDetectingView];
NSString *tapPointStr = NSStringFromCGPoint(tapPoint);
if (touch.tapCount == 1)
{
[self performSelector:#selector(notifyTap:) withObject:tapPointStr afterDelay:0.4];
// Make the afterDelay value bigger in order to have more chances of detecting a double tap and thus being able to cancel the single tap event, or make it smaller if you don't care about double taps and want to get the tap event as soon as possible
}
else if (touch.tapCount > 1)
{
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(notifyTap:) object:tapPointStr];
}
}
- (void)notifyTap:(NSString *)tapPointStr
{
CGPoint tapPoint = CGPointFromString(tapPointStr);
[tapDetectedDelegate userDidTapView:tapPoint];
}
#end
2. Check you have a window declared in your app delegate
You should have something like this in YourAppDelegate.h. Don't change the name of the property!
#interface YourAppDelegate : UIResponder <UIApplicationDelegate>
{
// ...
}
// ...
// The app delegate must implement the window property if it wants to use a main storyboard file
#property (nonatomic) UIWindow *window;
#end
3. Override the window property of your app delegate
Just like this, which should be in YourAppDelegate.m. Again, don't change the name of the method!
// Replace the default UIWindow property with a TapDetectingWindow
- (TapDetectingWindow *)window
{
static TapDetectingWindow *tapDetectingWindow = nil;
if (!tapDetectingWindow)
{
tapDetectingWindow = [[TapDetectingWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
}
return tapDetectingWindow;
}
4. Set the delegate protocol in your view controller
Be sure to adopt the tap handling protocol in your MainViewController.h
#import "TapDetectingWindow.h"
#interface MainViewController : UIViewController <TapDetectingWindowDelegate, ...>
{
// ...
}
// ...
#end
5. Set up the tap detecting window and tap event handler
Specify your webview (actually any UIView should work) and tap event handler in your view controller's (void)viewDidLoad method
- (void)viewDidLoad
{
// ...
// Allow tap detection in webview
TapDetectingWindow *tapDetectingWindow = (TapDetectingWindow*)[(YouTubeRPAppDelegate*)[[UIApplication sharedApplication] delegate] window];
tapDetectingWindow.tapDetectingView = self.webView; // Your UIWebView
tapDetectingWindow.tapDetectedDelegate = self;
}
6. Handle the tap event as you wish
Just implement the userDidTapView method in your view controller
- (void)userDidTapView:(CGPoint)tapPoint
{
NSLog(#"Tap detected in webview at %#", NSStringFromCGPoint(tapPoint));
}
Most of the approaches deal with a complicated pair of UIView and UIWebView subclasses and overrode -touchesBegan:withEvent: etc. methods.
This JavaScript-based approach intercepts touches on the web DOM itself, and it seems like a clever way to sidestep the more complex process. I haven't tried it myself, but I'm curious to know the results, if you give it a shot.
I'm not sure of the exact implementation details but you need to subclass UIWindow and override sendEvent:. Then you can capture tap events and handle them accordingly, before they get down to the web view. Hope this points you in the right direction!
Create a UIView sub class containing the whole UIWebView. Then the event hitTest will be fired on touching the webview. Note: Don't put anything to the container except the webView.
#interface myWebViewContainer : UIView
... ...
#end
Then override the hitTest event:
-(UIView*) hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
NSLog(#"Hit test %#", [event description]);
UIView * returnView = [super hitTest:point withEvent:event];
[delegate userDidTapWebView];// notify the delegate that UIWebView is being touched by some naughty user :P
return returnView;
}
A much simpler approach since iOS 3.2 is simply to use a UITapGestureRecognizer.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UITapGestureRecognizer* tapGestureRecognizer = [[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(userDidTapWebView)] autorelease];
[self.pdfWebView addGestureRecognizer:tapGestureRecognizer];
}
- (void)userDidTapWebView {
NSLog(#"TAP");
}