ios WKWebView 没有完成加载,当 didFinishNavigation 被调用时 - WKWebView 中的错误?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30291534/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
WKWebView didn't finish loading, when didFinishNavigation is called - Bug in WKWebView?
提问by shrutim
Goal: To take a screenshot of WKWebView after the website finished loading
目标:在网站加载完成后截取 WKWebView 的截图
Method employed:
采用的方法:
- Defined a WKWebView var in UIViewController
Created an extension method called screen capture() that takes image of WKWebView
Made my UIViewController to implement WKNavigationDelegate
Set the wkwebview.navigationDelegate = self ( in the UIViewController init)
Implemented the didFinishNavigation delegation func in UIViewcontroller to call screen capture extension method for WKWebView
- 在 UIViewController 中定义了一个 WKWebView 变量
创建了一个名为 screen capture() 的扩展方法,用于拍摄 WKWebView 的图像
让我的 UIViewController 实现 WKNavigationDelegate
设置 wkwebview.navigationDelegate = self (在 UIViewController init 中)
在 UIViewcontroller 中实现了 didFinishNavigation 委托函数来调用 WKWebView 的截屏扩展方法
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) { let img = webView.screenCapture() }
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) { let img = webView.screenCapture() }
Questions:
问题:
- When I debug i simulator, I notice that the control reaches the didFinishNavigation() func even though the website has not yet rendered in the WKWebView
- Correspondingly the image screenshot taken is a white blob.
- 当我调试模拟器时,我注意到控件到达了 didFinishNavigation() 函数,即使该网站尚未在 WKWebView 中呈现
- 相应地,截取的图像截图是一个白色的斑点。
What am I missing here? I looked at all possible delegate functions for WKWebView and nothing else seem to represent the completion of content loading in WKWebView. Would appreciate help on if there is a work around
我在这里缺少什么?我查看了 WKWebView 的所有可能的委托函数,但似乎没有其他东西代表 WKWebView 中内容加载的完成。如果有解决办法,将不胜感激
Update: Adding screenshot code that I am using to take a screenshot for web view
更新:添加我用来截取网页视图屏幕截图的屏幕截图代码
class func captureEntireUIWebViewImage(webView: WKWebView) -> UIImage? {
var webViewFrame = webView.scrollView.frame
if (webView.scrollView.contentSize != CGSize(width: 0,height: 0)){
webView.scrollView.frame = CGRectMake(webViewFrame.origin.x, webViewFrame.origin.y, webView.scrollView.contentSize.width, webView.scrollView.contentSize.height)
UIGraphicsBeginImageContextWithOptions(webView.scrollView.contentSize, webView.scrollView.opaque, 0)
webView.scrollView.layer.renderInContext(UIGraphicsGetCurrentContext())
var image:UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
webView.scrollView.frame = webViewFrame
return image
}
return nil
}
采纳答案by matt
WKWebView doesn't use delegation to let you know when content loading is complete (that's why you can't find any delegate method that suits your purpose). The way to know whether a WKWebView is still loading is to use KVO (key-value observing) to watch its loading
property. In this way, you receive a notification when loading
changes from true
to false
.
WKWebView 不使用委托来让您知道内容加载何时完成(这就是为什么您找不到任何适合您目的的委托方法)。知道一个 WKWebView 是否还在加载的方法是使用 KVO(键值观察)来观察它的loading
属性。这样,当你收到通知loading
的变化true
来false
。
Here's a looping animated gif showing what happens when I test this. I load a web view and respond to its loading
property through KVO to take a snapshot. The upper view is the web view; the lower (squashed) view is the snapshot. As you can see, the snapshot does capture the loaded content:
这是一个循环动画 gif,显示了我测试时会发生什么。我加载一个 web 视图并loading
通过 KVO响应它的属性来拍摄快照。上视图是网页视图;较低的(压扁的)视图是快照。如您所见,快照确实捕获了加载的内容:
[NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
if (self->_webKitView.isLoading == true) {
NSLog(@"Still loading...");
}else {
NSLog(@"Finished loading...");
[timer invalidate];
dispatch_async(dispatch_get_main_queue(), ^{
[self->_activityIndicator stopAnimating];
});
}
}];
回答by Santhosh R
For those still looking for an answer to this, the marked answer is BS, he just forced his way into getting it accepted.
"loading" and webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) both do the same thing, indicate if the main resource is loaded. Now that does not mean the entire webpage/website is loaded, because it really depends on the implementation of the website. If it needs to load scripts and resources (images, fonts etc) to make itself visible, you'll still see nothing after the navigation is completed, because the network calls made by the website are not tracked by the webview, only the navigation is tracked, so it wouldn't really know when the website loaded completely.
对于那些仍在寻找答案的人来说,标记的答案是 BS,他只是强迫自己接受它。
"loading" 和 webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) 都做同样的事情,指示是否加载了主要资源。现在这并不意味着整个网页/网站都被加载了,因为它真的取决于网站的实现。如果它需要加载脚本和资源(图像、字体等)以使其可见,导航完成后你仍然看不到任何东西,因为网站所做的网络调用不会被 webview 跟踪,只有导航是跟踪,所以它不会真正知道网站何时完全加载。
回答by Esqarrouth
Here is how I solved it:
这是我解决它的方法:
class Myweb: WKWebView {
func setupWebView(link: String) {
let url = NSURL(string: link)
let request = NSURLRequest(URL: url!)
loadRequest(request)
addObserver(self, forKeyPath: "loading", options: .New, context: nil)
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
guard let _ = object as? WKWebView else { return }
guard let keyPath = keyPath else { return }
guard let change = change else { return }
switch keyPath {
case "loading":
if let val = change[NSKeyValueChangeNewKey] as? Bool {
if val {
} else {
print(self.loading)
//do something!
}
}
default:break
}
}
deinit {
removeObserver(self, forKeyPath: "loading")
}
}
Update Swift 3.1
更新 Swift 3.1
override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let _ = object as? WKWebView else { return }
guard let keyPath = keyPath else { return }
guard let change = change else { return }
switch keyPath {
case "loading":
if let val = change[NSKeyValueChangeKey.newKey] as? Bool {
//do something!
}
default:
break
}
}
回答by Martin-Gilles Lavoie
Lots of hand waiving in here, for incomplete solutions. The observer on "loading" is not reliable because when it's switched to NO, the layout hasn't happened yet and you can't get an accurate reading on page size.
很多人在这里放弃了不完整的解决方案。“加载”上的观察者不可靠,因为当它切换到 NO 时,布局尚未发生,您无法准确读取页面大小。
The injection of JS into the view to report page size can also be problematic depending on actual page content.
将 JS 注入视图以报告页面大小也可能存在问题,具体取决于实际页面内容。
The WKWebView uses, obviously, a scroller (a "WKWebScroller" subclass to be exact). So, your best bet is to monitor that scroller's contentSize.
显然,WKWebView 使用了一个滚动条(准确地说是“WKWebScroller”子类)。因此,最好的办法是监视该滚动条的 contentSize。
- (void) viewDidLoad {
//...super etc
[self.webKitView.scrollView addObserver: self
forKeyPath: @"contentSize"
options: NSKeyValueObservingOptionNew
context: nil];
}
- (void) dealloc
{
// remove observer
[self.webKitView.scrollView removeObserver: self
forKeyPath: @"contentSize"];
}
- (void) observeValueForKeyPath: (NSString*) keyPath
ofObject: (id) object
change: (NSDictionary<NSKeyValueChangeKey,id>*) change
context: (void*) context
{
if ([keyPath isEqualToString: @"contentSize"])
{
UIScrollView* scroller = (id) object;
CGSize scrollFrame = scroller.contentSize;
NSLog(@"scrollFrame = {%@,%@}",
@(scrollFrame.width), @(scrollFrame.height));
}
}
Watch out for the contentSize: it gets triggered A LOT. If your webView is embedded into another scrolling area (like if you're using it as a form element) then when you scroll your entire view, that subview webView will trigger the changes for the same values. So, make sure you dont resize needlessly or cause needless refreshes.
注意 contentSize:它被触发了很多。如果您的 webView 嵌入到另一个滚动区域(就像您将其用作表单元素一样),那么当您滚动整个视图时,该子视图 webView 将触发相同值的更改。因此,请确保不要不必要地调整大小或导致不必要的刷新。
This solution tested on iOS 12 on iPad Air 2 sim off High Sierra XCode 10.
此解决方案在 iPad Air 2 sim off High Sierra XCode 10 上的 iOS 12 上进行了测试。
回答by Kris Roofe
It's not a good choice to check if the page content loaded from swift or objective-c especially for very complex page with many dynamic content.
检查页面内容是从 swift 还是objective-c 加载并不是一个好的选择,尤其是对于具有许多动态内容的非常复杂的页面。
A better way to inform you ios code from webpage's javascript. This make it very flexible and effective, you can notify ios code when a dom or the page is loaded.
从网页的 javascript 中通知您 ios 代码的更好方法。这使它非常灵活和有效,您可以在加载 dom 或页面时通知 ios 代码。
You can check my posthere for further info.
你可以在这里查看我的帖子以获取更多信息。