如何判断 UIWebView 何时完成渲染(未加载)?

发布于 2024-11-03 13:19:30 字数 234 浏览 0 评论 0原文

我知道它何时完成加载...(webViewDidFinishLoad),但我想用来

[webView.layer renderInContext:UIGraphicsGetCurrentContext()];

从 UIWebView 创建图像。有时我会在 webView 完成渲染之前获取图像。我可以使用performSelector来延迟图像的获取,但是等待的时间是任意的并且很脆弱。

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 performSelector to delay the get of the image, but the amount of wait is arbitrary and brittle.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

撩心不撩汉 2024-11-10 13:19:30

这可能取决于您需要将视图渲染到的图形上下文类型,但您可以调用

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

它,这显然会欺骗 UIWebView 认为它正在被打印。如果您的最终目标是捕获完整页面,这可能会有所帮助。我们最近遇到了一个问题,即使页面完全加载,如果其中一些页面在屏幕外,调用普通的旧 -drawRect: 也不会渲染整个页面。

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.

冷血 2024-11-10 13:19:30

我在完全控制内容的应用程序中遇到了类似的问题。如果您从网络加载任意页面,这将不起作用,但如果您知道您的高级 DOM 结构并且它不会发生太大变化,它可能会起作用。

对我有用的是以下内容。

我必须递归地找到第一个 UIWebViewUIWebOverflowScrollView 类型的子视图,其框架为 (0, 0, 1024, 768)。如果我找不到,则表明内容尚未呈现。
找到它后,我检查此视图layer.sublayers.count。当渲染完成时,我总是会得到一到三个子层。但是,如果渲染尚未完成,内容视图始终至多一个子层。

现在,这是我的 DOM 结构所特有的,但如果您比较渲染前后的子层树,您可能会构建一个类似的“测试”。对我来说,经验法则是“第一个递归找到的 UIWebOverflowScrollView 类型的子视图将至少有三个子层”,对你来说它可能会有所不同。

无论如何,如果您决定使用这种方法,请务必小心,因为即使您不会因为查看 UIWebView 的视图和图层层次结构而被拒绝,这种行为也是不可靠的,而且很可能会发生。在 iOS 的未来版本中进行更改。 iOS 5 和 iOS 6 之间也很可能不一致。

最后,MonoTouch 的代码片段应该可以直接转换为 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;
    }
}

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;
    }
}
可爱咩 2024-11-10 13:19:30

使用 window.onload 或 jQuery $(document).ready 事件来触发 shouldStartLoad 回调怎么样?

类似的事情:

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

在你的 UIWebViewDelegate 中做类似的事情:

- (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
    }
}

使用这个方法,你可以将每个可能的事件从 JS 代码转发到你的 obj-c 代码。

希望有帮助。代码示例是在浏览器中编写的,因此未经测试! ;-)

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

盛夏已如深秋| 2024-11-10 13:19:30
- (void)webViewDidFinishLoad:(UIWebView *)webView {
   if (webView.isLoading)
        return;
    else
    {
            [self hideProgress];
    }

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

}
韬韬不绝 2024-11-10 13:19:30

如果您有权访问 HTML 文件并可以对其进行编辑 - 那么只需添加一些特殊变量即可告诉代码渲染已完成。
然后使用方法 stringByEvaluatingJavaScriptFromString 来了解您是否应该开始某些操作。
下面的例子很脏,但希望它能帮助你并给你带来想法:

(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 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 
}
有深☉意 2024-11-10 13:19:30
if (webView.loading == YES)
        {
            //Load image
        }

类似的东西可能会起作用。

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

Something like that might work.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文