我需要知道,当 UIWebView 完全加载网页时。我的意思是,当所有重定向都完成并且动态加载的内容准备好时。我尝试注入 javascript(查询document.readyState == 'complete'
),但这似乎不是很可靠。
是否有可能来自私有 api 的事件会给我带来结果?
我一直在寻找这个问题的答案,我从塞巴斯蒂安的问题中得到了这个想法。不知何故,它对我有用,也许对那些遇到这个问题的人有用。
我为 UIWebView 设置了一个委托,在 webViewDidFinishLoad 中,我通过执行 Javascript 来检测 webview 是否真的完成了加载。
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if ([[webView stringByEvaluatingJavaScriptFromString:@"document.readyState"] isEqualToString:@"complete"]) {
// UIWebView object has fully loaded.
}
}
我的解决方案:
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
if (!webView.isLoading) {
// Tada
}
}
UIWebView 执行 Javascript 的能力与页面是否已加载无关。它可能使用 stringByEvaluatingJavaScriptFromString: 在您首先调用加载 UIWebView 中的页面之前执行 Javascript,或者根本不加载页面。
因此,我无法理解如何接受此链接中的答案:webViewDidFinishLoad: Firing too early? 因为调用任何 Javascript 根本不会告诉您任何事情(如果他们正在调用一些有趣的 Javascript,例如监控 dom,他们不会提及这一点,如果有的话,因为它很重要)。
您可以调用一些 Javascript,例如,检查 dom 的状态或已加载页面的状态,并将其报告回调用代码,但是如果它报告页面尚未加载,那么您会做什么?- 稍后您将不得不再次调用它,但要多久,何时,何地,如何,多久,......。轮询通常不是解决任何问题的好方法。
知道页面何时完全加载并准确并控制确切知道其内容的唯一方法是自己执行此操作 - 将 JavaScript 事件侦听器附加到正在加载的页面并让它们调用您的 shouldStartLoadWithRequest: 与一些专有网址。例如,您可以创建一个 JS 函数来侦听窗口加载或 dom 就绪事件等,具体取决于您是否需要知道页面何时加载,或者是否仅 dom 已加载等。取决于您的需要。
如果网页不是您的,那么您可以将此 javascript 放入一个文件并将其注入您加载的每个页面。
如何创建 javascript 事件监听器是标准的 javascript,与 iOS 没有什么特别的关系,例如这里是 JavaScript 来检测 dom 何时以可以从 UIWebView 内注入加载页面的表单中加载,然后导致对 shouldStartLoadWithRequest: 的调用:(这将调用 shouldStartLoadWithRequestwhen:dom 已完成加载,即在显示完整页面内容之前,检测这只是更改事件侦听器)。
var script = document.createElement('script');
script.type = 'text/javascript';
script.text = function DOMReady() {
document.location.href = "mydomain://DOMIsReady";
}
function addScript()
{
document.getElementsByTagName('head')[0].appendChild(script);
document.addEventListener('DOMContentLoaded', DOMReady, false);
}
addScript();
WKWebViewdidFinish
在 DOM 的 readyState 为 时调用委托interactive
,这对于加载了 javascripts 的网页来说还为时过早。我们需要等待complete
状态。我创建了这样的代码:
func waitUntilFullyLoaded(_ completionHandler: @escaping () -> Void) {
webView.evaluateJavaScript("document.readyState === 'complete'") { (evaluation, _) in
if let fullyLoaded = evaluation as? Bool {
if !fullyLoaded {
DDLogInfo("Webview not fully loaded yet...")
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: {
self.waitUntilFullyLoaded(completionHandler)
})
} else {
DDLogInfo("Webview fully loaded!")
completionHandler()
}
}
}
}
进而:
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
DDLogInfo("Webview claims that is fully loaded")
waitUntilFullyLoaded { [weak self] in
guard let unwrappedSelf = self else { return }
unwrappedSelf.activityIndicator?.stopAnimating()
unwrappedSelf.isFullLoaded = true
}
}
Martin H 的答案是在正确的轨道上,但它仍然可以被多次解雇。
您需要做的是向页面添加一个 Javascript onLoad 侦听器,并跟踪您是否已经插入了侦听器:
#define kPageLoadedFunction @"page_loaded"
#define kPageLoadedStatusVar @"page_status"
#define kPageLoadedScheme @"pageloaded"
- (NSString*) javascriptOnLoadCompletionHandler {
return [NSString stringWithFormat:@"var %1$@ = function() {window.location.href = '%2$@:' + window.location.href; window.%3$@ = 'loaded'}; \
\
if (window.attachEvent) {window.attachEvent('onload', %1$@);} \
else if (window.addEventListener) {window.addEventListener('load', %1$@, false);} \
else {document.addEventListener('load', %1$@, false);}",kPageLoadedFunction,kPageLoadedScheme,kPageLoadedStatusVar];
}
- (BOOL) javascriptPageLoaded:(UIWebView*)browser {
NSString* page_status = [browser stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"window.%@",kPageLoadedStatusVar]];
return [page_status isEqualToString:@"loaded"];
}
- (void)webViewDidFinishLoad:(UIWebView *)browser {
if(!_js_load_indicator_inserted && ![self javascriptPageLoaded:browser]) {
_js_load_indicator_inserted = YES;
[browser stringByEvaluatingJavaScriptFromString:[self javascriptOnLoadCompletionHandler]];
} else
_js_load_indicator_inserted = NO;
}
页面加载完成后,Javascript 监听器将触发 URL 的新页面加载 pageloaded:<URL that actually finished>
。然后您只需要更新shouldStartLoadWithRequest
以查找该 URL 方案:
- (BOOL)webView:(UIWebView *)browser shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if([request.URL.scheme isEqualToString:kPageLoadedScheme]){
_js_load_indicator_inserted = NO;
NSString* loaded_url = [request.URL absoluteString];
loaded_url = [loaded_url substringFromIndex:[loaded_url rangeOfString:@":"].location+1];
<DO STUFF THAT SHOULD HAPPEN WHEN THE PAGE LOAD FINISHES.>
return NO;
}
}
您也可以在这里使用接受的答案:UIWebView - How to identify the "last" webViewDidFinishLoad message? ...
基本上,使用estimatedProgress 属性,当达到100 时,页面已完成加载......请参阅本指南以获取帮助。
虽然它确实使用了一个私有框架,但根据指南,App Store 中有一些应用程序使用它......
使您的视图控制器成为 UIWebView 的代表。(您可以在 Interface Builder 中执行此操作,也可以在 viewDidLoad 方法中使用此代码:
[self.myWebView setDelegate:self];
然后使用此处接受的答案中描述的方法:webViewDidFinishLoad: Firing too early? .
编码:
-(void) webViewDidFinishLoad:(UIWebView *)webView
{
NSString *javaScript = @"function myFunction(){return 1+1;}";
[webView stringByEvaluatingJavaScriptFromString:javaScript];
//Has fully loaded, do whatever you want here
}
多亏了 JavaScript,新资源和新图形可以永远加载——确定加载何时“完成”,对于足够变态的页面,将等于解决停止问题。您真的不知道页面何时加载。
如果可以,只需显示页面或其中的图像,并使用 webViewDidLoad 更新图像,并让用户决定何时切断 - 或不切断。
这是@jasonjwwilliams 答案的简化版本。
#define JAVASCRIPT_TEST_VARIBLE @"window.IOSVariable"
#define JAVASCRIPT_TEST_FUNCTION @"IOSFunction"
#define LOAD_COMPLETED @"loaded"
// Put this in your init method
// In Javascript, first define the function, then attach it as a listener.
_javascriptListener = [NSString stringWithFormat:
@"function %@() {\
%@ = '%@';\
};\
window.addEventListener(\"load\", %@, false);",
JAVASCRIPT_TEST_FUNCTION, JAVASCRIPT_TEST_VARIBLE, LOAD_COMPLETED, JAVASCRIPT_TEST_FUNCTION];
// Then use this:
- (void)webViewDidFinishLoad:(UIWebView *)webView;
{
// Only want to attach the listener and function once.
if (_javascriptListener) {
[_webView stringByEvaluatingJavaScriptFromString:_javascriptListener];
_javascriptListener = nil;
}
if ([[webView stringByEvaluatingJavaScriptFromString:JAVASCRIPT_TEST_VARIBLE] isEqualToString:LOAD_COMPLETED]) {
NSLog(@"UIWebView object has fully loaded.");
}
}
由于所有这些答案都是旧的并且webViewDidFinishLoad
已经被贬值,而且后来有很多令人头疼的问题,让我解释一下如何使用 swift 4.2准确知道页面何时在 webkit (webview) 中完成加载
class ViewController: UIViewController, WKNavigationDelegate {
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
webView.navigationDelegate = self
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// it is done loading, do something!
}
}
当然,我遗漏了一些其他默认代码,因此您要添加到现有功能中。
尝试类似的东西 https://github.com/Buza/uiwebview-load-completion-tracker 它帮助我加载谷歌街景