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

Posted

技术标签:

【中文标题】如何判断 UIWebView 何时完成渲染(未加载)?【英文标题】:How do I tell when a UIWebView is done rendering (not loading)? 【发布时间】:2011-04-27 19:07:46 【问题描述】:

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

[webView.layer renderInContext:UIGraphicsGetCurrentContext()];

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

【问题讨论】:

我遇到了同样的问题。我在 webViewDidFinishLoading 上做了一个 renderInContext,它将 web 视图的先前状态呈现到上下文中。奇怪的是,我第一次在 web 视图中加载内容时,屏幕截图被正确呈现。只有在第一次内容加载后,它才开始表现出上述行为 我不认为有这样做的好方法。我尝试通过 stringByEvaluatingjavascriptFromString 注入一些 javascript 来回调我的 Obj-C 代码,但它并不比任意延迟更可靠。 :-( 我同意。我做了一个延迟的 performSelector,正如问题中的 tillerstarr 所描述的那样,这是浪费时间并给出不一致的结果。我碰巧在我的 UIWebView 中运行了 jQuery,它有一个触发的“就绪”事件,并且应该比常规的 Javascript onLoad 更可靠。我想从理论上讲,可以让“就绪”事件触发某种类型的链接活动,该活动会触发 webView:shouldStartLoadWithRequest:navigationType。你觉得这值得一试吗? 【参考方案1】:

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

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

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

【讨论】:

【参考方案2】:

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

对我有用的是以下内容。

我不得不递归地找到UIWebView 类型为UIWebOverflowScrollView 的第一个子视图,其框架为(0, 0, 1024, 768)。如果我找不到,那肯定是内容尚未呈现的迹象。 找到它后,我检查了 此视图的 layer.sublayers.count。渲染完成后,我总是会得到一到三个子层。但是,如果渲染尚未完成,则内容视图总是有至多一个子层。

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

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

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

【讨论】:

【参考方案3】:

使用 window.onload 或 jQuerys $(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 代码。

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

【讨论】:

我也有同样的问题。您提出的解决方案不起作用,JS 不知道页面何时实际完成渲染。 @1800INFORMATION 当我发布这个答案时,这个解决方案早在 2011 年就奏效了。在不知道您对我的提议的具体问题的情况下,我建议您在分配href 之前查看最新版本的jQuery 的documentation,或者尝试监听DOMContentLoaded 事件。【参考方案4】:
- (void)webViewDidFinishLoad:(UIWebView *)webView 
   if (webView.isLoading)
        return;
    else
    
            [self hideProgress];
    


【讨论】:

【参考方案5】:

如果您可以访问 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 

【讨论】:

在无意义的while 循环中阻塞 UI 线程是个坏主意。此外,它没有回答这个问题:从 iOS 6 开始,渲染是异步完成的,因此无法确定何时从 JavaScript 完成渲染。【参考方案6】:
if (webView.loading == YES)
        
            //Load image
        

这样的事情可能会奏效。

【讨论】:

为什么webView.loading 会在webViewDidFinishLoad: 中返回YES

以上是关于如何判断 UIWebView 何时完成渲染(未加载)?的主要内容,如果未能解决你的问题,请参考以下文章

如何判断何时未使用谷歌位置自动完成建议?

角度-知道模板渲染何时完成-使用异步管道

检测用户何时点击加载在 UIWebView 或 WKWebView 中的 web 表单

知道何时在 UIWebView 中加载了 AJAX

Android Webview:检测渲染何时完成

检测 UIWebView 何时加载引用文件失败