views:

1398

answers:

2

I have an application that uses UIWebViews in several view controllers. The UIWebViews are used to render locally generated html, no slow network access required.

To save memory I only load these on demand as prompted by the viewcontroller viewWillAppear callback. (And unload offscreen instances in response to didReceiveMemoryWarning messages.

The problem is that the user gets to see the html being rendered, sometimes accompanied by flashes of styling and assorted unpleasant artifacts. I would much rather the rendering be done offscreen, and reveal the fully rendered view when its ready.

It would be very tidy to be able to have the viewWillAppear not return until the UIWebView is fully rendered. But how?

I tell the UIWebView what to render by sending it a loadHTMLString:baseURL message. This is synchronous, and some time (soon) later the webview's delegate gets called back webViewDidFinishLoad.

I experimented with running a runloop inside viewWillAppear, running either the NSDefaultRunLoopMode or UITrackingRunLoopMode. This works in the simulator (it complains to the log "[CATransaction synchronize] called within transaction" but does work) but on a device it deadlocks, with webViewDidFinishLoad never being called.

(Also, it seems like the UIWebView loading property doesn't work. At least, after I call loadHTMLString:baseURL and before getting the callback it's not true.)

+3  A: 

Lots of solutions here I think. A quick one is to load your UIWebView with it's hidden property set to YES. Then set your UIViewController as the UIWebViews delegate and implement:

- (void)webViewDidFinishLoad:(UIWebView *)webView

where you set the property back to NO.

A thing to note is that webViewDidFinishLoad will fire more than once if you have framed/embedded content. So you have to keep track of this. Shouldn't really be a problem if you are loading local content.

monowerker
This solution has the user looking at a blank display while the page is drawn offscreen. (For my application I have a background that the transparent page will be rendered in front of, so that's something to look at, but...)It would be better to model other apps that show the control element (button or list item) pressed while the page is rendered, and only then have the fully rendered view animate in.
cluesque
Then have the target/selector of the button drive the rendering, and advance the page when it finishes (webViewDidFinishLoad: as monowerker suggests)
Rob Napier
A: 

I like monowerker's solution best, but another solution would be to hold onto the already-rendered UIWebView all the time (in some more permanent object than the view controller). I'd only do that if the look of monowerker's solution is too disruptive.

Rob Napier
The UIWebView consumes a lot of memory, and is the first thing thrown overboard when we get a low memory warning.Also one of my use cases is rendering detail of an object selected from a list view (e.g., in Mail you see a list of messages, tap one and read the whole thing) so I can't render until I know which object is to be viewed.
cluesque