我需要知道当UIWebView完全加载了网页时的状态,这意味着当所有重定向完成并动态加载的内容准备好时。
我尝试注入JavaScript(查询document.readyState =='complete'
),但这似乎不是非常可靠。
也许有一个私有API事件可以给我带来结果吗?
我需要知道当UIWebView完全加载了网页时的状态,这意味着当所有重定向完成并动态加载的内容准备好时。
我尝试注入JavaScript(查询document.readyState =='complete'
),但这似乎不是非常可靠。
也许有一个私有API事件可以给我带来结果吗?
我一直在寻找这个答案,从Sebastian的问题中得到了这个想法。不知何故这对我有用,也许对那些遇到这个问题的人也有用。
我给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 soon?,因为调用任何JavaScript并没有告诉你任何有用的信息(如果他们调用了一些有趣的JavaScript,例如监视dom,他们没有提到,但如果有的话,他们会因为其重要性而提到)。
你可以调用一些JavaScript,例如检查dom或已加载页面的状态,并将其返回给调用代码,但是如果它报告页面尚未加载,则该怎么办? - 你需要再过一段时间再次调用它,但是多久之后,何时,何地,多频繁,...轮询通常不是任何问题的好解决方案。
唯一可靠且可以掌控的方法是自己知道页面何时完全加载。在加载的页面中添加JavaScript事件监听器,让它们调用您的shouldStartLoadWithRequest:并传递一些专有URL。例如,你可以创建一个JS函数来监听window load或dom ready事件等,具体取决于你需要知道页面何时加载还是仅需知道dom何时加载等。如果网页不是您的,则可以将此JavaScript放入文件中并注入到您加载的每个页面中。
如何创建JavaScript事件监听器是标准JavaScript,与iOS无关,例如下面是检测dom何时加载的JavaScript代码,可在UIWebView中注入到正在加载的页面中,并导致调用shouldStartLoadWithRequest:(这会在dom完成加载后但在完整页面内容显示之前调用shouldStartLoadWithRequestwhen,要检测这一点,只需更改事件监听器即可)。
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();
当DOM的readyState状态为interactive
时,WKWebView会调用didFinish
委托方法,但对于加载了大量Javascript的网页来说,这个时间点太早了。我们需要等待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
}
}
#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;
}
pageloaded:<已完成加载的 URL>
。接下来,您只需要更新 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;
}
}
BOOL _js_load_indicator_inserted = NO;
,但是当调用webView:shouldStartLoadWithRequest:navigationType:
时,request.URL.scheme
从未等于kPageLoadedScheme(@"pageloaded")。 - kraftydevil[self.myWebView setDelegate:self];
然后使用此处接受的答案中描述的方法:webViewDidFinishLoad: Firing too soon?。
代码:
-(void) webViewDidFinishLoad:(UIWebView *)webView
{
NSString *javaScript = @"function myFunction(){return 1+1;}";
[webView stringByEvaluatingJavaScriptFromString:javaScript];
//Has fully loaded, do whatever you want here
}
这是@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!
}
}