Home > Tutorials > Adding Swiping to UIWebView using JavaScript (iOS4/iOS5) and custom URL scheme

Adding Swiping to UIWebView using JavaScript (iOS4/iOS5) and custom URL scheme

Hey folks, welcome to my new Tutorial, which will cover the topic of how to implement swiping in a UIWebView using JavaScript and will explain how to send variables back to our UIWebView using Custom URL Schemes.

The code itself is straightforward but will cover a lot of cool things.

To load the JavaScript code we call in our webViewHasFinishedLoading Delegate method the following code:


#define DEVICE_IOS5()   (([[[UIDevice currentDevice] systemVersion] floatValue]>=5.0))

if (DEVICE_IOS5()){

     [self.webView stringByEvaluatingJavaScriptFromString:@"loadListenerIOS5()"];

} else {

     [self.webView stringByEvaluatingJavaScriptFromString:@"loadListenerIOS4()"];

}

This is the JavaScript code:

File: SwipeEvent.js

Use your local html to let the browser know about it. I showed how to do that in my last tutorial.

         EDIT: One more thing. When copying JavaScript files from the Finder to XCode, it will not treat it as an added resource but as a file to compile. Of course that is not possible so it will give you warnings back and the file will not be copied to the resource folder on your device / simulator. To fix that problem, go to target->build settings and than drag and drop the file from Compile Sources to Copy Bundle Resources.


var startX;

var startY;

var diffX;

var diffY;

var hypo;

var isIOS5;

function loadListenerIOS4(){

    var body = document.getElementsByTagName("body")[0];

    body.addEventListener("touchstart", touchStart, false);

    body.addEventListener("touchend", touchEnd, false);

    body.addEventListener("touchmove", touchMove, false);

    isIOS5=0;

    return "JS Listener iOS4 loaded";

}

function loadListenerIOS5(){

    var body = document.getElementsByTagName("body")[0];

    body.addEventListener("touchstart", touchStart, false);

    body.addEventListener("touchend", touchEnd, false);

    body.addEventListener("touchmove", touchMove, false);

    isIOS5=1;

    return "JS Listener iOS5 loaded";

}

function fireSwipeScheme(a,b,c){

    var scheme="swipe:" + a + ":" + b + ":" + c;

    document.location=scheme;

}

function touchStart(event){

    diffX=0;

    diffY=0;

    if (isIOS5){

        startX=event.pageX;

        startY=event.pageY;

    } else {

        startX=event.touches[0].pageX;

        startY=event.touches[0].pageY;

    }

}

function touchEnd(event){

    if (diffX>50||diffX<-50){

        fireSwipeScheme(diffX,diffY,hypo);

    }

}

function touchMove(event) {

    var a;

    var b;

    if (isIOS5){

        a = event.pageX - startX;

        b = event.pageY - startY;

    } else {

        a = event.touches[0].pageX - startX;

        b = event.touches[0].pageY - startY;

    }

    var c = calcHypotenuse(a,b);

    diffX=a;

    diffY=b;

    hypo=c;

}

function calcHypotenuse(a,b){

    return Math.sqrt(a*a,b*b);

}

LoadListenerIOS4 and LoadListeneriOS5 are there to attach an event listener to the body object of our DOM. The first is a constant which can be either “touchstart”, “touchend”, “touchmove” or “touchcancel” and the second determines the function we are about to call. So in this case, “touchstart” calls touchStart( … ).

Both loaders are the same, only that one sets the variable isIOS5 to 1 and the other sets it to 0. This is important if you look into the touchStart function.

On iOS5, to get the current x/y coordinates of a touch, some must call event.pageX and event.pageY, while on iOS4, its event.touches[0].pageX and event.touches[0].pageY.

The same goes for all the other touch function.

So what we do here is, we register the touch coordinates on touch start, calculate all the way through touchmove the current distance and fire immediately an url scheme to the UIWebView if the touches ended.

fireSwipeScheme is also pretty interesting.

The variable “scheme” is a combination about the keyword “swipe” and a, b, c which are actually the variables diff x, diff y and the hypotenuse.

So, how do we now get these variables into our objective C Code?

In the delegate method webViewShouldStartLoadOnRequest: withNavigationType:  we add the following code

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{

    NSString*scheme=[[request URL]absoluteString];

    //NSLog(@"received scheme: %@", scheme);

    NSArray *components=[scheme componentsSeparatedByString:@":"];

    if ([[components objectAtIndex:0]isEqualToString:@"swipe"]){
        [self handleSwipeInWebView:components];
        return NO;
     }

     return YES;

}

And to our class we add the following method:


#define kMinSwipeDistanceHorizontalInWebView 100.0f

#define kMaxSwipeDistanceVerticalInWebView 50.0f

-(void)handleSwipeInWebView: (NSArray*)code{

    @autoreleasepool {

        NSInteger swipeDistX=[[NSNumber numberFromString:]intValue];

        NSInteger swipeDistY=abs([[NSNumber numberFromString:]intValue]);

        NSLog(@"swipeDistY: %d", swipeDistY);

        NSLog(@"swipeDistX: %d", swipeDistX);

        if ((swipeDistX>kMinSwipeDistanceHorizontalInWebView||swipeDistX<-kMinSwipeDistanceHorizontalInWebView)&&swipeDistY>kMaxSwipeDistanceVerticalInWebView){

            MasterViewController*ptr=(MasterViewController*)self.masterViewController;

            if (ptr){

                 if (swipeDistX<-kMinSwipeDistanceHorizontalInWebView){

                     [ptr moveDownInTreeHierarchy];

                 } else if (swipeDistX&gt;kMinSwipeDistanceHorizontalInWebView){

                     [ptr moveUpInTreeHierarchy];

                 }

           }

     }

}

In the delegate method, the UIWebView catches all clicked links events or all document.location events from JavaScript. By sending a custom URL script we can first return NO, to make sure that no new page will get loaded and we can use it actually as an encoder for our variables. The url scheme in this example looks something like this

 

Swipe:-40:30:-50.

In the delegate method, I break the array into components by separating them by the ASCII character ‘:’. In the handleSwipeInWebView method, I can easily use the components than to let my code do what I want him to do.

In my case, I am firing the swipe Event from the DetailViewController in my SplitViewController and notify the MasterViewController about it.

 

  1. January 12, 2012 at 11:55 pm

    Hi Markus – this is exactly what I need, do you have a example project I can use to explore how you get this to work in detail?

    • January 14, 2012 at 6:58 pm

      Hey , i dont have any actual example project because the code was part of a way bigger project and is also 1000 times more complicated now. If you have any questions tho, i will help you of course.

      Thanks for replying btw :=)

      Groetjes

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: