如何判断 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 何时完成渲染(未加载)?的主要内容,如果未能解决你的问题,请参考以下文章