web-dev-qa-db-ja.com

UIWebView:ページが実際にロードを完了したのはいつですか?

UIWebViewによってWebページが完全にロードされたときを知る必要があります。本当に完全に、すべてのリダイレクトが完了し、動的にロードされたコンテンツの準備ができたときです。 JavaScriptを挿入してみました(document.readyState == 'complete')、しかし、それは非常に信頼できるとは思えません。

多分、私に結果をもたらすプライベートAPIからのイベントはありますか?

28
Sebastian

私はこの答えを探していましたが、セバスチャンの質問からこのアイデアを得ました。どういうわけかそれは私にとってはうまくいきますが、おそらくこの問題に遭遇した人にとってはうまくいくでしょう。

UIWebViewにデリゲートを設定し、webViewDidFinishLoadで、Javascriptを実行することでWebViewのロードが本当に完了したかどうかを検出します。

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    if ([[webView stringByEvaluatingJavaScriptFromString:@"document.readyState"] isEqualToString:@"complete"]) {
        // UIWebView object has fully loaded.
    }
}
46
Fedry Kemilau

私の解決策:

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    if (!webView.isLoading) {
        // Tada
    }
}
22
tgf

JavaScriptを実行するUIWebViewの機能は、ページがロードされたかどうかに依存しません。 stringByEvaluatingJavaScriptFromString:を使用すると、最初にUIWebViewでページを読み込む呼び出しを行う前に、またはページをまったく読み込まずにJavaScriptを実行できます。

したがって、このリンクの答えがどのように受け入れられるのか理解できません: webViewDidFinishLoad:Firing too soon?anyJavascript ' t何かを教えてください(たとえば、domを監視している興味深いJavascriptを呼び出している場合は、それについて言及せず、重要だからと言ってもありません)。

たとえば、domの状態やロードされたページの状態を調べ、呼び出しコードに報告するJavascriptを呼び出すことができますが、ページがまだロードされていないことを報告する場合はどうしますか? -少し後でもう一度呼び出す必要がありますが、どれだけ後、いつ、どこで、どのように、どのくらいの頻度で..。

ページが完全にロードされたときを正確に把握し、自分で何をするかを正確に制御する唯一の方法-ロードされるページにJavaScriptイベントリスナーをアタッチして、shouldStartLoadWithRequest:を独自の方法で呼び出しますurl。たとえば、ページがいつロードされたか、またはdomだけがロードされたかなどを知る必要があるかどうかに応じて、ウィンドウロードやdom readyイベントなどをリッスンするJS関数を作成できます。ニーズに応じて。

Webページが自分のものでない場合は、このjavascriptをファイルに入れて、ロードするすべてのページに挿入できます。

Javascriptイベントリスナーの作成方法は標準のjavascriptであり、iOSには特に関係ありません。たとえば、UIWebView内から読み込みページに挿入できるフォームにdomが読み込まれたときに検出するJavaScriptです。 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();
10
Gruntcakes

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;
    }
}
4
jasonjwwilliams

ここで受け入れられた答えを使用することもできます: IWebView-「最後の」webViewDidFinishLoadメッセージを識別する方法? ...

基本的に、timatedProgressプロパティを使用し、それが100に達すると、ページの読み込みが完了します...ヘルプについては このガイド を参照してください。

ガイド によれば、プライベートフレームワークを使用しますが、App Storeにはそれを使用するアプリがいくつかあります...

3
gtmtg

WKWebViewは、DOMのreadyStateがdidFinishであるときにinteractiveデリゲートを呼び出します。これは、javascriptでロードされたWebページには早すぎます。 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
    }
}
2
konradowy

View ControllerをUIWebViewのデリゲートにします。 (Interface Builderでこれを行うか、viewDidLoadメソッドで次のコードを使用します。

[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
}
2
gtmtg

JavaScriptのおかげで、新しいリソースと新しいグラフィックが永遠に読み込まれる可能性があります。読み込みが「完了」した場合、十分な倒錯度のページに対して、 halting problem を解決することになります。ページがいつ読み込まれるかは本当にわかりません。

可能であれば、ページまたはその画像を表示し、webViewDidLoadを使用して画像を更新し、ユーザーにいつ切り取るかを決定させます。

1
Neal

これが@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.");
    }
}
0
Chris Prince

これらの答えはすべて古く、webViewDidFinishLoadは減価されており、多くの頭痛の種が後で説明します正確に Swift 4.2

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!

    }

}

もちろん、他のデフォルトコードの一部が欠落しているため、既存の関数に追加しています。

0
Cesar Bielich