views:

286

answers:

2

I'm loading a biggish (and javascript-heavy) page into a UIWebView, and I want to add a UIActivityView to spin while it thinks.

Problem is, my - (void)webViewDidFinishLoad:(UIWebView *)webView method gets called quite a while before the rendering actually happens. Enough so that my spinner (which set to hide when stopped) never actually shows up. By the time the UI is even assembled, the spinner has already been stopped and hidden, even though there's enough time to wonder if it's broken before the UIWebView actually gets the goods to the screen.

I wish there was a "webViewDidFinishRendering", but that would imply that WebKit is something other than lickedy-split fast... ;-)

Thoughts? Perhaps I should toss the thing up and set a timer to come stop it, and unhook that from anything that's actually happening in the WebView?

A: 

Yeah, as you've found, webViewDidFinishLoad tells you when data has been loaded into the view, but gives no information about what JS might be going on once that's done. Unfortunately UIWebView is pretty limited. The best you can probably do is to poll it with a JS query, via stringByEvaluatingJavaScriptFromString. If you can run some JS to test whether things are finished in the web view, you could do that every half second and dismiss the spinner when you get a positive result.

Tom Harrington
+2  A: 

Here's how I handle cases where I need to be sure the page is fully loaded: Add a javascript event handler for the document.onload event that does something like this:

function onDocumentLoad() {
     window.location.href = 'myapp://loaded';
}

Then in your UIWebViewDelegate, you can do this:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    if ([[request.URL absoluteString] isEqual:@"myapp://loaded"]) {
        [activityIndicator stopAnimating];
        return NO;
    }
    return YES;
}
cduhn
Neat! I'll try that!
Dan Ray
Sadly, that gets fired right about when the webViewDidFinish... method does. And then it's a good few seconds later the WebView finishes rendering the map.
Dan Ray
You say that the page is javascript heavy. Is the rendering delay caused by javascript that runs when the DOM or page finishes loading? If so, there's probably some other point at which you can inject the window.location.href assignment to stop your spinner at the last possible moment. For example, if you're loading data asynchronously via AJAX, there's probably some javascript callback that gets triggered when the data finishes loading. It's just a matter of figuring out the right place to inject the code for your specific content.
cduhn
Well, I'm not sure I have that much say over it--I'm calling the Google Maps API. Why that instead of a UIMapView? Because my client insists on seeing Google-Maps-style navigation paths in that screen. The only way I can figure to do that is with the GMAPI in a WebView.I'll poke around their docs some and see if there's anything I can hook into when they've finished their work.
Dan Ray
Great! Couple things: inside Google Maps API, what you want to do is hook to the directionservice.route() function and set up a callback to do the window.location.href assignment.Then, in your webViewShouldStartLoadWithRequest call, you have to do some casting, because NSURL doesn't compare nicely with an NSString containing the URL. Silly, but it doesn't. I had to build a stringWithFormat from the string value of the NSURL. But it works! Thanks!
Dan Ray
You're right. My code example should have said [request.URL absoluteString]. I'll edit it for the record. Glad you got it working anyway. Now as long as Google doesn't change their javascript out from under you you'll be all set.
cduhn
For completeness, you're also missing a right-bracket after `isEqual:@"myapp://loaded"`. Thanks again for your help.
Dan Ray
Whoops. Fixed. Thanks.
cduhn