Archive

Archive for April, 2010

Saving an Image from UIWebView

April 30th, 2010

IMG_0049.jpg

I spent the last few days trying to wrap my head around how to save a user selected image in a UIWebView. The concept itself would seem really simple, and if you’re using Safari on the iPad or iPhone it is really simple. You just tap and hold on an image. Simple. Problem is, when you’re developing you’re own application, this functionality doesn’t exist in UIWebView. Don’t believe me? Try it out for yourself. Go ahead… I’ll wait.

Ok, so if you’re still here you must have found out I’m right. The worst part of all this is I found out the hard way… from a user. I rarely save images from the web, so it never occurred to me to try it in my own app (and honestly I thought this was built in freebie functionality). Well, I got an email the other day from a user who was shocked that this didn’t work. He was not nearly as shocked as I was. I was even more shocked to find out that there’s pretty much nobody on the web who has discussed how to do this. Much time was spent searching for a solution, a couple tweets sent with only crickets responding, time was spent filling out a bug report with Apple (radar) only to have it closed without a work around as a duplicate. So, I basically hit a brick wall.

Alright, let’s get down to it. My complaining is done. Well not really. Did you also know its a huge PITA to handle touches in a UIWebView? Like to the point where you’re either having to sub-class UIWindow to trap touches or you’re having to add a view above the UIWebView. I’m going to leave it up to you as to how you want to implement what we’re talking about. I personally decided to not bother with any of it. Why? Well, I’ve already started building in a bunch of gestures, so it just made sense use a gesture recognizer instead of adding in a bunch of code which may or may not end up breaking sooner than later. My hope is that Apple fixes the problem, but I’m not holding my breath. Anyway, choose for yourself. For this example we’ll just follow the KISS method.

Ok, here come the code. First things first, you need to have some kind of way to collect touches. For simplicity we’ll use a UITapGestureRecognizer:

UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doubleTap:)];
doubleTap.numberOfTouchesRequired = 2;
[self.webView addGestureRecognizer:doubleTap];
[doubleTap release];

What we’ve done is simply define a gesture (two fingers on tap the screen at the same time) and add that to the webview. When the webview detects two fingers on the screen (tap) it’ll fire the doubleTap method which is where we’ll figure out what image has been selected (if any at all).

- (void) doubleTap:(UITapGestureRecognizer *)sender {
	int scrollPosition = [[self.webView stringByEvaluatingJavaScriptFromString:@"window.pageYOffset"] intValue];
	CGPoint startPoint = [sender locationInView:self.webView];
	NSString *js = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).tagName", startPoint.x, startPoint.y+scrollPosition];
	NSString *value = [self.webView stringByEvaluatingJavaScriptFromString:js];
	if ([value isEqualToString:@"IMG"]) {
		NSString *imgURL = [NSString stringWithFormat:@"document.elementFromPoint(%f, %f).src", startPoint.x, startPoint.y+scrollPosition];
		NSString *urlToSave = [self.webView stringByEvaluatingJavaScriptFromString:imgURL];
		}

//Do whatever you want - like a UIAlertView or UIPopover to let the user chose what to do with the image
}

So, let’s walk through the code. The first thing we need to get is where we are on the page. Without this vital chunk of information we end up sending the same coordinates to the document so even if we’re at the bottom of the web page, when we tap the top of the screen it sends the coordinates for the header image. Not good. Next we get the location of the tap in the webView itself. After that we determine which element is at the location where the user tapped. Notice we added the scroll position to the y coordinate so again we don’t end up with the same x,y all the time. ‘tagName’ returns back the element type – ‘A’, ‘IMG’, ‘IFRAME’ etc. We only want images right now (you could always grab the a ref if you wanted to do other fun stuff) so we throw an if statement in to check if the element is an image. If it is, we grab the URL of the element and do stuff with it.

When that code is all said and done you’ll end up with an NSString like so:
http://justanotheripadblog.com/wp-content/uploads/2010/04/3GiPad_thumb.jpg
Nice right?

So, that’s how I ended up saving images from a UIWebView without resorting to private API calls or other wacky business.

Sound Off: Did this work for you? Am I crazy and there was a much easier way? Please let me know in the comments. Thanks!

Credits: iCab Blog for the elementFromPoint idea – see Alexander’s comment on 9/1 11:55am, and the support folks at perfectbrowser.com who got me started looking at doing this through javascript.

brandon Coding, iPhone

Twitter links powered by Tweet This v1.6.1, a WordPress plugin for Twitter.