开发者

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

开发者 https://www.devze.com 2023-03-01 16:08 出处:网络
I know when its done loading... (webViewDidFinishLoad), but I want to use [webView.layer renderInContext:UIGraphicsGetCurrentContext()];

I know when its done loading... (webViewDidFinishLoad), but I want to use

[webView.layer renderInContext:UIGraphicsGetCurrentContext()];

to create an image from the UIWebView. Occasionally I get the image prior to the webView finishing its rendering. I can use performS开发者_开发百科elector to delay the get of the image, but the amount of wait is arbitrary and brittle.


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! ;-)


- (void)webViewDidFinishLoad:(UIWebView *)webView {
   if (webView.isLoading)
        return;
    else
    {
            [self hideProgress];
    }

}


If you have access to the HTML file and can edit it - then just add some special variable which will tell the code that rendering is done.
Then use method stringByEvaluatingJavaScriptFromString to know should you start some actions or not.
The example below is pretty dirty, but hope it will help and give you the idea:

(void)webViewDidFinishLoad:(UIWebView *)webView
{
    BOOL renderingDone = NO;
    while (!(renderingDone == [[webView stringByEvaluatingJavaScriptFromString:@"yourjavascriptvariable"] isEqualToString:@"YES"]))
    {
        // the app will be "freezed" till the moment when your javascript variable will not get "YES" value 
    }
    // do your stuff, rendering is done 
}


if (webView.loading == YES)
        {
            //Load image
        }

Something like that might work.

0

精彩评论

暂无评论...
验证码 换一张
取 消