如何知道UIWebView何时完成渲染(不加载)?

我知道什么时候它完成加载…(webViewDidFinishLoad),但我想使用

[webView.layer renderInContext:UIGraphicsGetCurrentContext()]; 

从UIWebView创build一个图像。 偶尔我会在webView完成其渲染之前获取图像。 我可以使用performSelector延迟图像的获取,但等待的数量是任意的和脆弱的。

这可能取决于您需要呈现的视图的graphics上下文种类,但您可以调用

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

这显然欺骗UIWebView认为它正在打印。 如果您的最终目标是捕捉整个页面,这可能会有所帮助。 我们最近遇到了这个问题,即使页面已经完全加载,调用plain old -drawRect:如果页面的一部分不在屏幕上,那么就不会渲染整个页面。

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

对我有效的是以下几点。

我不得不recursion地find第一个UIWebViewtypes为UIWebOverflowScrollView的子视图( UIWebOverflowScrollView (0, 0, 1024, 768) 。 如果我找不到一个,这是一个肯定的迹象,内容还没有呈现。 find它,我检查这个视图的 layer.sublayers.count 。 当渲染结束时,我总是会有一到三个子层。 但是,如果渲染没有完成,则内容视图最多只有一个子图层。

现在, 这是特定于我的DOM结构 – 但是如果您比较渲染之前和之后的子图层树,可能会构成一个类似的“testing”。 对我来说,经验法则是“ UIWebOverflowScrollViewtypes的第一个recursion发现的子视图将至less有三个子层”,对你来说可能会有所不同。

无论如何,如果你决定使用这种方法,要非常小心,因为即使你不会因为查看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; } } 

那么使用window.onload或jQuerys $(document).ready事件来触发shouldStartLoadcallback呢?

就像是:

 $(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代码。

希望有所帮助。 代码示例是在浏览器中编写的,因此未经testing! 😉

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

如果你有权访问HTML文件并且可以编辑它 – 那么只需添加一些特殊的variables来告诉代码渲染完成。
然后使用方法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 (webView.loading == YES) { //Load image } 

像这样的东西可能工作。