How do I tell when a UIWebView is done rendering (not loading)? How do I tell when a UIWebView is done rendering (not loading)? ios ios

How do I tell when a UIWebView is done rendering (not loading)?


This may depend upon the kind of graphics context you need the view rendered into, but you can call

- (void)drawRect:(CGRect)area forViewPrintFormatter:(UIViewPrintFormatter *)formatter

which apparently tricks the UIWebView into thinking that it's being printed. This may help if your ultimate goal is to capture the complete page. We've had the problem recently in which even if the page was fully loaded, calling plain old -drawRect: didn't render the entire page if some of it was offscreen.


I had a similar problem in an application where I fully control the content. This won't work if you load arbitrary pages from the web but it may work if you know your high-level DOM structure and it doesn't change much.

What worked for me was the following.

I had to recursively find the first UIWebView's subview of type UIWebOverflowScrollView with a frame of (0, 0, 1024, 768). If I couldn't find one, that was a sure sign the content hasn't been rendered yet.Having found it, I check this view's layer.sublayers.count. When the rendering finishes, I would always end up with one or three sublayers. If the rendering hasn't finished, however, the content view always had at most one sublayer.

Now, that's specific to my DOM structure—but it may be possible that you make up a similar “test” if you compare sublayer tree before and after rendering. For me, the rule of thumb was “the first recursively found subview of type UIWebOverflowScrollView will have at least three sublayers”, for you it will likely be different.

Anyway, take great care if you decide to use this approach, for even though you won't get rejected for looking into UIWebView's view and layer hierarchy, this kind of behaviour is unreliable and is very likely to change in future versions of iOS. It may very well be inconsistent between iOS 5 and iOS 6 as well.

Finally, code snippets for MonoTouch which should be straightforward to translate to Objective C:

bool IsContentScrollView (UIScrollView scrollView){    return scrollView.Frame.Equals (new RectangleF (0, 0, 1024, 768));}[DllImport ("/usr/lib/libobjc.dylib")]private static extern IntPtr object_getClassName (IntPtr obj);public static string GetClassName (this UIView view) {    return Marshal.PtrToStringAuto (object_getClassName (view.Handle));}bool IsWebOverflowScrollView (UIScrollView scrollView){    return scrollView.GetClassName () == "UIWebOverflowScrollView";}IEnumerable<UIScrollView> ScrollViewsInside (UIView view){    foreach (var subview in view.Subviews) {        foreach (var scrollView in ScrollViewsInside (subview).ToList())            yield return scrollView;        if (subview is UIScrollView)            yield return (UIScrollView)subview;    }}bool CanMakeThumbnail {    get {        var scrollViews = ScrollViewsInside (this).Where (IsWebOverflowScrollView).ToList ();        var contentView = scrollViews.FirstOrDefault (IsContentScrollView);        // I don't know why, but this seems to be a good enough heuristic.        // When the screen is black, either contentView is null or it has just 1 sublayer.        // This may *break* on any iOS updates or DOM changes--be extra careful!        if (contentView == null)            return false;        var contentLayer = contentView.Layer;        if (contentLayer == null || contentLayer.Sublayers == null || contentLayer.Sublayers.Length < 3)            return false;        return true;    }}



What about using the window.onload or jQuerys $(document).ready event to trigger the shouldStartLoad callback?

Something like:

$(document).ready(function(){    location.href = "app://do.something"})

and in your UIWebViewDelegate do something like:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{    NSURL *url = request.URL;    if([url.host isEqualToString:@"app"]){        //maybe check for "do.something"        //at this point you know, when the DOM is finished    }}

With this method you can forward every possible event from the JS code to your obj-c code.

Hope that helps. The code sample is written in the browser, and therefore not tested! ;-)