Implementing Mail within my iOS app - iphone

I've currently got an iOS app where an item is randomly chosen from an array of items and is displayed on the screen. I've already built the "randomness" and display functionality into the app, but am now trying to set it up so that you can email the item from the array that is currently on the screen from within the app. For example, you hit the button and it randomly displays a number from 1-10. I'd like to be able for the user to email whatever number is randomly shown on the screen, with the email body pre-populated with the number on the screen. So, the user gets the number "3", hit's the email button and when the email compose comes up "3" is already pre-populated in the body.
I am having two issues, first is figuring out how to implement the email function code within my current code. I've already built a tester app that has a button that triggers the email compose to appear, and populate the body with some static text, so I've got a basic understanding of how the code works, but I do not know how to integrate it with the code I've already written.
My second issue is getting the body of the mail message to be pre-populated with the random number from the screen.
For the first problem here is what my ViewController.h looks like (I've already add the MessageUI framework)
#import <UIKit/UIKit.h>
#import <MessageUI/MessageUI.h>
#interface ViewController : UIViewController {
NSArray *testArray;
}
- (IBAction)buttonGo:(UIButton *)sender;
#property (strong, nonatomic) IBOutlet UILabel *testLabel;
#property (strong, nonatomic) NSArray *testArray;
- (void) makePrediction;
#end
My ViewController.m looks like
#import "ViewController.h"
#import <MessageUI/MessageUI.h>
#interface ViewController ()
#end
#implementation ViewController
#synthesize testArray;
#synthesize testLabel;
- (void)viewDidLoad
{
[super viewDidLoad];
self.testArray = [[NSArray alloc] initWithObjects:#"number one",#"number `two",#'numberthree", nil];`
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)buttonGo:(id)sender {
NSUInteger index = arc4random_uniform(self.testArray.count);
self.testLabel.text = [self.testArray objectAtIndex:index];
}
- (void) makePrediction {
NSUInteger index = arc4random_uniform(self.
testArray.count);
self.testLabel.text = [self.testArray objectAtIndex:index];
}
- (BOOL) canBecomeFirstResponder {
return YES;
}
- (void) motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
self.testLabel.text = #"";
}
- (void) motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event {
if ( motion == UIEventSubtypeMotionShake ){
[self makePrediction];
}
}
- (void) motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event {
NSLog(#"motion cancelled");
}
#end
The app runs fine, but I'm unsure where to implement my email code. I'm also unsure how to populate my email body with the random choice from my array. I am assuming it will have something to do with this bit of MessageUI
NSString * sentFrom = #"text in email body";
[myMail setMessageBody:sentFrom isHTML:YES];

This is the place to do your job as you will be holding text
- (IBAction)buttonGo:(id)sender
and for second issue
Note isHTML has to be set NO in case your text doesnt use it.
MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc] init];
[mailController setSubject:#"randomNumber"];
[mailController setMessageBody:self.ideaLabel.text isHTML:NO];

Related

OpenCV video processing tutorial issue

I followed the iOS video processing tutorial on the OpenCV site and managed to compile and install it on the device. However when I click on the "Start" button, nothing visible happens. Apparently neither my - (void)processImage:(Mat&)image callback is never called. Could you give me some hints what could be the problem?
Some things I checked:
actionStart is called, and both log lines prints YES
the self object is valid when assigned to self.videoCamera.delegate
imageView is valid and visible on the screen
videoCamera is initialized when its start method is called
I use a iPhone 3GS for testing.
Here is the full source of my ViewController.h:
#import <UIKit/UIKit.h>
#import <opencv2/highgui/cap_ios.h>
#interface ViewController : UIViewController <CvVideoCameraDelegate>
{
IBOutlet UIImageView* imageView;
IBOutlet UIButton* button;
CvVideoCamera* videoCamera;
}
- (IBAction)actionStart:(id)sender;
#property (nonatomic, strong) CvVideoCamera* videoCamera;
#end
and ViewController.mm:
#implementation ViewController
#synthesize videoCamera;
- (void)viewDidLoad
{
[super viewDidLoad];
self.videoCamera = [[CvVideoCamera alloc] initWithParentView:imageView];
self.videoCamera.delegate = self;
self.videoCamera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront;
self.videoCamera.defaultAVCaptureSessionPreset = AVCaptureSessionPreset352x288;
self.videoCamera.defaultAVCaptureVideoOrientation = AVCaptureVideoOrientationPortrait;
self.videoCamera.defaultFPS = 30;
self.videoCamera.grayscaleMode = NO;
}
#pragma mark - UI Actions
- (IBAction)actionStart:(id)sender;
{
[self.videoCamera start];
NSLog(#"video camera running: %d", [self.videoCamera running]);
NSLog(#"capture session loaded: %d", [self.videoCamera captureSessionLoaded]);
}
#pragma mark - Protocol CvVideoCameraDelegate
- (void)processImage:(cv::Mat&)image
{
// Do some OpenCV stuff with the image
cv::Mat image_copy;
cvtColor(image, image_copy, CV_BGRA2BGR);
// invert image
bitwise_not(image_copy, image_copy);
cvtColor(image_copy, image, CV_BGR2BGRA);
}
#end
I tested on an iPhone 4 and it works well. Maybe the iPhone 3GS doesn't have a front facing camera? Then you have to change this line: self.videoCamera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionFront to self.videoCamera.defaultAVCaptureDevicePosition = AVCaptureDevicePositionBack

endless loop adding a subview

I am a little confused, and after countless attempts and read several articles I decided to write.
my problem is that if you call a method from a class (xml) and it is aimed at viewcontroller all goes well
but if I might add [self.view add...] it back to the top reloading the viewDidLoad of the viewController class entering into an endless loop.
this is what I do
class (ViewController)
.h
#import <UIKit/UIKit.h>
#class XMLStuff;
#interface skiSpeedViewController : UIViewController {
}
#property (nonatomic, retain) XMLStuff *xml;
.m
- (void)viewDidLoad
{
[super viewDidLoad];
xml.skiSpeedC = self;
GpsStuff *gps = [GpsStuff alloc];
[gps init];
}
gps.m
- (id)init
{
self = [super init];
if (self) {
xml = [XMLStuff alloc];
}
}
-(void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
[xml lon:newLocation.coordinate.longitude lat:newLocation.coordinate.latitude];
xml.h
#import "skiSpeedViewController.h"
#class skiSpeedViewController;
#interface XMLStuff : NSObject <NSXMLParserDelegate> {
}
#property (retain, nonatomic) skiSpeedViewController *skiSpeedC;
.m
#synthesize skiSpeedC;
- (void) parserDidEndDocument:(NSXMLParser *)parser {
NSLog(#"--%#", self.skiSpeedC); // Return (null)
[self.skiSpeedC riceviDic:datiMeteo];
}
ViewController.m
-(void)riceviDic:(NSMutableDictionary *)dictMeteo {
datiMeteo = [[NSMutableDictionary alloc]initWithDictionary:dictMeteo];
}
}
- (void) parserDidEndDocument:(NSXMLParser *)parser {
classViewController *skiSpeedC = [classViewController alloc];
[skiSpeedC riceviDic:datiMeteo];
}
You are creating a new instance of classViewController every time. Your "xml" class (XMLStuff?) should have a pointer to the view controller and be calling the riceviDic method on that instance.
You're getting an infinite loop because when you allocate the XML object in viewDidLoad, it too starts parsing the XML, then creates more XML objects, which then create more viewControllers...
So, add a property to XMLStuff of type classViewController, and when you create it in viewDidLoad:
xml.skiSpeedC = self;
Then, in parserDidEndDocument:
- (void) parserDidEndDocument:(NSXMLParser *)parser {
[self.skiSpeedC riceviDic:datiMeteo];
}
UPDATE
OK, after your edit things look very different - you seem to have introduced a new class - GpsStuff, which has its own instance of XMLStuff (and a dodgy looking init method which I assume you haven't copied in properly?). Which one is actually parsing your document? XMLStuff in your view controller, or in GPSStufF? I'm guessing the one in GPSStuff, which you haven't set the skiSpeedC property for. I was previously assuming that you were calling everything from your view controller.
Why not remove the creation of a new XMLStuff object from GPSStuff, and when you create GPSStuff in your view controller, pass the xml object you've created into it:
- (void)viewDidLoad
{
[super viewDidLoad];
GpsStuff *gps = [[GpsStuff alloc] init];
XMLStuff *xml = [[XMLStuff alloc] init];
xml.skiSpeedC = self;
gps.xml = xml;
[xml release];
}
Also, the skiSpeedC property should probably not be retain, since it is essentially a delegate assignment and the view controller is not going to be released before you release the xml parser.
As a note, by convention you should be initializing objects like this:
GPSStuff *gps = [[GPSStuff alloc] init];
Not on two lines. You want what is returned from init to be assigned to your variable.

Using UITouch To Drag A UITextField Around The Screen on iPhone

I am developing a sample application, where I have a situation for moving a UITextFields, UILabels etc., across the screen. I am not able to find any resource for implementing this.
I want to implement like this for a UITextField
Since you have an excellent tutorial on dragging already, I will assume your problem is the keyboard that comes up when you try to drag a UITextField instead of a UIView. The solution should be pretty simple: myTextField.userInteractionEnabled = NO; - that should disable user interaction and make it "read only". Perhaps have an edit mode where all text fields get this flag set. If it causes trouble, then add the textField to a UIView and then set the userInteractionEnabled to false. Then you can drag the UIView and it will drag the text field with it.
I followed Michael's suggestion and got the solution.I have given the code snippets below which will be useful for those who need to implement the same.
Steps:
Choose windows based application,then create a UIViewController subclass and add it to the window in the appdelegate file.
In the XIB of the viewcontroller class you created add UIViews and add the controls like textfield etc.,to the UIViews that you have created.We are going to move these views only,so add IBOutlets in the .h file of the view controller subclass and map them to the IB accordingly.
Sample Code
appdelegate.h
#import <UIKit/UIKit.h>
#import "MyView.h"
#interface MyAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
MyView *viewController;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#end
appdelegate.m
#import "MyAppDelegate.h"
#implementation MyAppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after application launch.
viewController=[[MyView alloc]init];
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
- (void)dealloc {
[viewController release];
[window release];
[super dealloc];
}
#end
viewcontroller.h
#import <UIKit/UIKit.h>
#interface MyView : UIViewController {
IBOutlet UIView *textFieldView;
IBOutlet UIView *labelView;
}
#end
viewcontroller.m
#import "MyView.h"
#implementation MyView
- (void)viewDidLoad {
[self.view addSubview:textFieldView];
[self.view addSubview:labelView];
[super viewDidLoad];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// get touch event
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if ([touch view] == textFieldView) {
// move the image view
textFieldView.center = touchLocation;
}
if ([touch view] == labelView) {
// move the image view
labelView.center = touchLocation;
}
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
Thank u all.Have a niece time.

Objective-C address book

I have two Objective-C classes that inherit from the UIViewController and am trying a different approach at learning how to interact with the iPhone's address book. The example Apple provides assumes that everything is in one class, but this isn't the way I need it done. My objective would be to have the address book view close after a person is selected. Please have a look and let me know how i can accomplish this without having CallerClass implement ABPeoplePickerNavigationControllerDelegate. Thanks!
-- edit --
What it seems to be boiling down to is the [self dismissModalViewControllerAnimated:YES]; does not have any effect in CalleeClass.m. I still can't seem to get a reaction to close the address book from this command.
CallerClass.m
#import "CallerClass.h"
#implementation CallerClass
- (IBAction)openAddressBook {
CalleeClass *cc = [[CalleeClass alloc] init];
[self presentModalViewController:[cc doIt] animated:YES];
}
CalleeClass.h
#import <UIKit/UIKit.h>
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>
#interface CalleeClass : UIViewController <ABPeoplePickerNavigationControllerDelegate> {
NSString *name;
}
-(ABPeoplePickerNavigationController *)doIt;
#property (nontoxic, retain) NSString *name;
#end
CalleeClass.m
#import <UIKit/UIKit.h>
#import <AddressBook/AddressBook.h>
#import <AddressBookUI/AddressBookUI.h>
#import "CalleeClass.h"
#implementation CalleeClass
#synthesize name;
… (default ABPeoplePickerNaviationControllerDelegate implementation outside of what's listed)
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {}
return self;
}
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person {
self.name = (NSString *)ABRecordCopyValue(person,kABPersonAddressProperty);
[self dismissModalViewControllerAnimated:YES];
return NO;
}
-(ABPeoplePickerNavigationController *)doIt {
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
return picker;
}
#end
If the problem is, as you say, that [self dismissModalViewControllerAnimated:YES] has no effect if called from CalleeClass, this is because dismissModalViewControllerAnimated: must be called on the presenting view controller (i.e., the one on which you called presentModalViewController:Animated:. Since you don't have a reference to your CallerClass instance in CalleeClass, this doesn't work.
Fortunately, as the documentation for dismissModalViewControllerAnimated: notes:
If you call this method on the modal view controller itself, however, the modal view
controller automatically forwards the message to its parent view controller.
So this should work:
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person {
self.name = (NSString *)ABRecordCopyValue(person,kABPersonAddressProperty);
[peoplePicker dismissModalViewControllerAnimated:YES];
return NO;
}
Once you have identified the contact you want to act on you can just pass around the int32 recordID, although as mentioned in the API docs you should probably also use the composite name since as you will notice the recordID is a simple value that starts with "1" and you could run into trouble if your database was restored to a phone with new contacts in old recordID values. Every time you need to access something from the Address Book you do need to create the phone book but you can close it right after, so with recordID you can open, get what you want and then close it. My suggestion is just use the picker like a normal view controller up to the point where you get the recordID, dismiss it, then keep that unique identifier. Use picker again when you need to find a new recordID.

Detecting taps inside a UIWebView

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");
}