UIWebView - 如何识别“最后”webViewDidFinishLoad消息?

时间:2021-12-01 07:17:22

The webViewDidFinishLoad message seems to be sent each time any object in the page has been loaded. Is there a way to determine that all loading of content is done?

每次加载页面中的任何对象时,似乎都会发送webViewDidFinishLoad消息。有没有办法确定所有内容加载都已完成?

10 个解决方案

#1


9  

Interesting, I wouldn't have thought it would work like that. Although I'm sure there are other ways to do it (is there a way to extract the URL from the webViewDidFinishLoad message so that you can see which one is the main page finishing loading?), the main thing I can think of is using the estimatedProgress to check the progress of the page and fire off whatever you want to do when it's 100% finished loading, which is what I do in my app. Google "iphone webview estimatedprogress" and click the first link for a guide I wrote on how to do this.

有意思,我不会认为它会像那样工作。虽然我确定还有其他方法可以做到这一点(有没有办法从webViewDidFinishLoad消息中提取URL,以便您可以看到哪一个是主页完成加载?),我能想到的主要是使用estimatedProgress检查页面的进度,并在100%完成加载时触发你要做的任何事情,这就是我在我的应用程序中所做的。谷歌“iphone webview estimatedprogress”,然后点击我写的关于如何执行此操作的指南的第一个链接。

Update:

更新:

Please use phopkins' answer below instead of mine! Using private APIs in your apps is a bad idea and you will probably get rejected, and his solution is the right one.

请使用下面的phopkins答案而不是我的!在您的应用中使用私有API是一个坏主意,您可能会被拒绝,他的解决方案是正确的。

#2


60  

I'm guessing that iframes cause the webViewDidStartLoad / webViewDidFinishLoad pair.

我猜iframe会导致webViewDidStartLoad / webViewDidFinishLoad对。

The [webView isLoading] check mentioned as an answer didn't work for me; it returned false even after the first of two webViewDidFinishLoad calls. Instead, I keep track of the loading as follows:

提到的[webView isLoading]检查作为答案对我不起作用;即使在两次webViewDidFinishLoad调用中的第一次调用之后它返回false。相反,我跟踪加载如下:

- (void)webViewDidStartLoad:(UIWebView *)webView {
  webViewLoads_++;
}


- (void)webViewDidFinishLoad:(UIWebView *)webView {
  webViewLoads_--;

  if (webViewLoads_ > 0) {
    return;
  }

  …
}

- (void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error {
  webViewLoads_--;
}

(Note this will only work if the start / finished pairs don't come serially, but in my experience so far that hasn't happened.)

(注意这只有在开始/结束对不是连续出现的情况下才有效,但根据我迄今为止的经验尚未发生。)

#3


18  

Check this one, it will definitely work for you:

检查一下,它肯定会对你有用:

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

#4


1  

Another way to monitor load progress with less control is to observe the WebViewProgressEstimateChangedNotification, WebViewProgressFinishedNotification, and WebViewProgressStartedNotification notifications. For example, you could observe these notifications to implement a simple progress indicator in your application. You update the progress indicator by invoking the estimatedProgress method to get an estimate of the amount of content that is currently loaded.

使用较少控制来监视加载进度的另一种方法是观察WebViewProgressEstimateChangedNotification,WebViewProgressFinishedNotification和WebViewProgressStartedNotification通知。例如,您可以观察这些通知,以在您的应用程序中实现简单的进度指示器。您可以通过调用estimatedProgress方法来更新进度指示器,以估计当前加载的内容量。

from http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/WebKit/Classes/WebView_Class/Reference/Reference.html

来自http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/WebKit/Classes/WebView_Class/Reference/Reference.html

#5


1  

You can also use this short Category I wrote that adds blocks into UIWebView and also lets you choose between default UIWebView behavior (getting notified after each object load), or the "expected" behavior (getting notified only when the page has fully loaded).

您还可以使用我写的这个简短的类别,它将块添加到UIWebView中,还允许您在默认UIWebView行为(在每个对象加载后得到通知)或“预期”行为(仅在页面完全加载时获得通知)之间进行选择。

https://github.com/freak4pc/UIWebView-Blocks

https://github.com/freak4pc/UIWebView-Blocks

#6


1  

I needed to capture a variable from the page which was not fully loaded yet.

我需要从尚未完全加载的页面中捕获变量。

This worked for me:

这对我有用:

- (void) webViewDidFinishLoad:(UIWebView *)webView {
    NSString *valueID = [webView stringByEvaluatingJavaScriptFromString:@"document.valueID;"];

    if([valueID isEqualToString:@""]){
        [webView reload];
        return;
    }

    //page loaded
}

#7


1  

All of the options did not really work for my use case. I used phopkins example slightly modified. I found that if the HTML loaded into the webview contained an image that would be a separate request that happened serially so we have to account for that and I did that with a timer. Not the best solution but it seems to work.:

所有选项都不适用于我的用例。我用phopkins的例子略加修改。我发现,如果加载到webview中的HTML包含一个图像,该图像是一个连续发生的单独请求,所以我们必须考虑到这一点,我用计时器做了。不是最好的解决方案,但它似乎工作:

- (void)webViewActuallyFinished {
     _webViewLoads--;

    if (_webViewLoads > 0) {
        return;
    }

    //Actually done loading

}

- (void)webViewDidStartLoad:(UIWebView *)webView {
    _webViewLoads++;
}


- (void)webViewDidFinishLoad:(UIWebView *)webView {


    [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(webViewActuallyFinished) userInfo:nil repeats:NO];
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
  _webViewLoads--;
}

#8


0  

hi it may be a bit far back already but i hope this helps.

嗨它可能已经有点远了,但我希望这会有所帮助。

i just used a notification when it enters the webViewDidFinishLoad method so that i can capture one instance of the method and then i'll detect the notification and do my logic from there.

我只是在它进入webViewDidFinishLoad方法时使用了一个通知,这样我就可以捕获该方法的一个实例,然后我将检测通知并从那里做我的逻辑。

hope this helps. it does not capture the last called instance of the webViewDidFinishLoad method, but allows you to do something once it has been called and not be repeated calls to that particular method (eg. showing a button) too.

希望这可以帮助。它不会捕获webViewDidFinishLoad方法的最后一个被调用的实例,但允许您在调用后执行某些操作,而不是重复调用该特定方法(例如,显示按钮)。

*********EDIT*********

*********编辑*********

i did a test and managed to test it out. it actually works and the method called from the notification will be called after the full page has been loaded.

我做了一个测试,并设法测试它。它实际上是有效的,并且在加载完整页面后将调用从通知中调用的方法。

alternatively, i think you can do a counter on the delegate method webViewDidStartLoad and also on webViewDidFinishLoad to make sure that they are the same before you run your codes. this though, is an ugly hack as we will never know how many times it will be called unless like me, you are loading a html feed and can add a JavaScript to check how many elements there are on the page that you are loading. i'm just sharing some of the methods i have tried to solve this. hope it helps.

或者,我认为你可以在委托方法webViewDidStartLoad和webViewDidFinishLoad上做一个计数器,以确保在运行代码之前它们是相同的。虽然这是一个丑陋的黑客,因为我们永远不会知道它将被调用多少次,除非像我一样,你正在加载一个html提要并且可以添加一个JavaScript来检查你正在加载的页面上有多少元素。我只是分享了我试图解决这个问题的一些方法。希望能帮助到你。

feedback is encouraged. thanks!

鼓励反馈。谢谢!

#9


0  

Here's what I use to show a spinner while DOM loads, built on top of @Vinod's answer.

这是我在DOM加载时用来显示微调器的内容,建立在@Vinod的答案之上。

Note that between webViewDidStartLoad and webViewDidFinishLoad the readyState is completed from the previous request (if any), that's why the polling should begin after webViewDidFinishLoad.

请注意,在webViewDidStartLoad和webViewDidFinishLoad之间,readyState是从先前的请求(如果有的话)完成的,这就是为什么轮询应该在webViewDidFinishLoad之后开始。

readyState possible values are loading, interactive or completed (and maybe nil ?)

readyState可能的值是加载,交互还是完成(也许是零?)

- (void)webViewDidStartLoad:(UIWebView *)webView {
    [self spinnerOn];
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    [self startDOMCompletionPolling];
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
    [self startDOMCompletionPolling];
}


- (void)startDOMCompletionPolling {
    if (self.domCompletionListener) {
        [self.domCompletionListener invalidate];
    }
    self.domCompletionListener = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(checkDOMCompletion) userInfo:nil repeats:YES];
}

- (void)checkDOMCompletion {
    NSString *readyState = [self.webView stringByEvaluatingJavaScriptFromString:@"document.readyState"];

    if (readyState != nil) {
        if (readyState.length > 0) {
            if ([readyState isEqualToString:@"loading"]) { //keep polling
                return;
            }
        }
    }
    //'completed', 'interactive', nil, others -> hide spinner
    [self.domCompletionListener invalidate];
    [self spinnerOff];
}

#10


-5  

The way I do it is this:

我这样做是这样的:

- (void)webViewDidFinishLoad:(UIWebView *)webview  {
    if (webview.loading)
    return;
    // now really done loading code goes next
    [logic]
}

#1


9  

Interesting, I wouldn't have thought it would work like that. Although I'm sure there are other ways to do it (is there a way to extract the URL from the webViewDidFinishLoad message so that you can see which one is the main page finishing loading?), the main thing I can think of is using the estimatedProgress to check the progress of the page and fire off whatever you want to do when it's 100% finished loading, which is what I do in my app. Google "iphone webview estimatedprogress" and click the first link for a guide I wrote on how to do this.

有意思,我不会认为它会像那样工作。虽然我确定还有其他方法可以做到这一点(有没有办法从webViewDidFinishLoad消息中提取URL,以便您可以看到哪一个是主页完成加载?),我能想到的主要是使用estimatedProgress检查页面的进度,并在100%完成加载时触发你要做的任何事情,这就是我在我的应用程序中所做的。谷歌“iphone webview estimatedprogress”,然后点击我写的关于如何执行此操作的指南的第一个链接。

Update:

更新:

Please use phopkins' answer below instead of mine! Using private APIs in your apps is a bad idea and you will probably get rejected, and his solution is the right one.

请使用下面的phopkins答案而不是我的!在您的应用中使用私有API是一个坏主意,您可能会被拒绝,他的解决方案是正确的。

#2


60  

I'm guessing that iframes cause the webViewDidStartLoad / webViewDidFinishLoad pair.

我猜iframe会导致webViewDidStartLoad / webViewDidFinishLoad对。

The [webView isLoading] check mentioned as an answer didn't work for me; it returned false even after the first of two webViewDidFinishLoad calls. Instead, I keep track of the loading as follows:

提到的[webView isLoading]检查作为答案对我不起作用;即使在两次webViewDidFinishLoad调用中的第一次调用之后它返回false。相反,我跟踪加载如下:

- (void)webViewDidStartLoad:(UIWebView *)webView {
  webViewLoads_++;
}


- (void)webViewDidFinishLoad:(UIWebView *)webView {
  webViewLoads_--;

  if (webViewLoads_ > 0) {
    return;
  }

  …
}

- (void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error {
  webViewLoads_--;
}

(Note this will only work if the start / finished pairs don't come serially, but in my experience so far that hasn't happened.)

(注意这只有在开始/结束对不是连续出现的情况下才有效,但根据我迄今为止的经验尚未发生。)

#3


18  

Check this one, it will definitely work for you:

检查一下,它肯定会对你有用:

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

#4


1  

Another way to monitor load progress with less control is to observe the WebViewProgressEstimateChangedNotification, WebViewProgressFinishedNotification, and WebViewProgressStartedNotification notifications. For example, you could observe these notifications to implement a simple progress indicator in your application. You update the progress indicator by invoking the estimatedProgress method to get an estimate of the amount of content that is currently loaded.

使用较少控制来监视加载进度的另一种方法是观察WebViewProgressEstimateChangedNotification,WebViewProgressFinishedNotification和WebViewProgressStartedNotification通知。例如,您可以观察这些通知,以在您的应用程序中实现简单的进度指示器。您可以通过调用estimatedProgress方法来更新进度指示器,以估计当前加载的内容量。

from http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/WebKit/Classes/WebView_Class/Reference/Reference.html

来自http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/WebKit/Classes/WebView_Class/Reference/Reference.html

#5


1  

You can also use this short Category I wrote that adds blocks into UIWebView and also lets you choose between default UIWebView behavior (getting notified after each object load), or the "expected" behavior (getting notified only when the page has fully loaded).

您还可以使用我写的这个简短的类别,它将块添加到UIWebView中,还允许您在默认UIWebView行为(在每个对象加载后得到通知)或“预期”行为(仅在页面完全加载时获得通知)之间进行选择。

https://github.com/freak4pc/UIWebView-Blocks

https://github.com/freak4pc/UIWebView-Blocks

#6


1  

I needed to capture a variable from the page which was not fully loaded yet.

我需要从尚未完全加载的页面中捕获变量。

This worked for me:

这对我有用:

- (void) webViewDidFinishLoad:(UIWebView *)webView {
    NSString *valueID = [webView stringByEvaluatingJavaScriptFromString:@"document.valueID;"];

    if([valueID isEqualToString:@""]){
        [webView reload];
        return;
    }

    //page loaded
}

#7


1  

All of the options did not really work for my use case. I used phopkins example slightly modified. I found that if the HTML loaded into the webview contained an image that would be a separate request that happened serially so we have to account for that and I did that with a timer. Not the best solution but it seems to work.:

所有选项都不适用于我的用例。我用phopkins的例子略加修改。我发现,如果加载到webview中的HTML包含一个图像,该图像是一个连续发生的单独请求,所以我们必须考虑到这一点,我用计时器做了。不是最好的解决方案,但它似乎工作:

- (void)webViewActuallyFinished {
     _webViewLoads--;

    if (_webViewLoads > 0) {
        return;
    }

    //Actually done loading

}

- (void)webViewDidStartLoad:(UIWebView *)webView {
    _webViewLoads++;
}


- (void)webViewDidFinishLoad:(UIWebView *)webView {


    [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(webViewActuallyFinished) userInfo:nil repeats:NO];
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
  _webViewLoads--;
}

#8


0  

hi it may be a bit far back already but i hope this helps.

嗨它可能已经有点远了,但我希望这会有所帮助。

i just used a notification when it enters the webViewDidFinishLoad method so that i can capture one instance of the method and then i'll detect the notification and do my logic from there.

我只是在它进入webViewDidFinishLoad方法时使用了一个通知,这样我就可以捕获该方法的一个实例,然后我将检测通知并从那里做我的逻辑。

hope this helps. it does not capture the last called instance of the webViewDidFinishLoad method, but allows you to do something once it has been called and not be repeated calls to that particular method (eg. showing a button) too.

希望这可以帮助。它不会捕获webViewDidFinishLoad方法的最后一个被调用的实例,但允许您在调用后执行某些操作,而不是重复调用该特定方法(例如,显示按钮)。

*********EDIT*********

*********编辑*********

i did a test and managed to test it out. it actually works and the method called from the notification will be called after the full page has been loaded.

我做了一个测试,并设法测试它。它实际上是有效的,并且在加载完整页面后将调用从通知中调用的方法。

alternatively, i think you can do a counter on the delegate method webViewDidStartLoad and also on webViewDidFinishLoad to make sure that they are the same before you run your codes. this though, is an ugly hack as we will never know how many times it will be called unless like me, you are loading a html feed and can add a JavaScript to check how many elements there are on the page that you are loading. i'm just sharing some of the methods i have tried to solve this. hope it helps.

或者,我认为你可以在委托方法webViewDidStartLoad和webViewDidFinishLoad上做一个计数器,以确保在运行代码之前它们是相同的。虽然这是一个丑陋的黑客,因为我们永远不会知道它将被调用多少次,除非像我一样,你正在加载一个html提要并且可以添加一个JavaScript来检查你正在加载的页面上有多少元素。我只是分享了我试图解决这个问题的一些方法。希望能帮助到你。

feedback is encouraged. thanks!

鼓励反馈。谢谢!

#9


0  

Here's what I use to show a spinner while DOM loads, built on top of @Vinod's answer.

这是我在DOM加载时用来显示微调器的内容,建立在@Vinod的答案之上。

Note that between webViewDidStartLoad and webViewDidFinishLoad the readyState is completed from the previous request (if any), that's why the polling should begin after webViewDidFinishLoad.

请注意,在webViewDidStartLoad和webViewDidFinishLoad之间,readyState是从先前的请求(如果有的话)完成的,这就是为什么轮询应该在webViewDidFinishLoad之后开始。

readyState possible values are loading, interactive or completed (and maybe nil ?)

readyState可能的值是加载,交互还是完成(也许是零?)

- (void)webViewDidStartLoad:(UIWebView *)webView {
    [self spinnerOn];
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    [self startDOMCompletionPolling];
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
    [self startDOMCompletionPolling];
}


- (void)startDOMCompletionPolling {
    if (self.domCompletionListener) {
        [self.domCompletionListener invalidate];
    }
    self.domCompletionListener = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(checkDOMCompletion) userInfo:nil repeats:YES];
}

- (void)checkDOMCompletion {
    NSString *readyState = [self.webView stringByEvaluatingJavaScriptFromString:@"document.readyState"];

    if (readyState != nil) {
        if (readyState.length > 0) {
            if ([readyState isEqualToString:@"loading"]) { //keep polling
                return;
            }
        }
    }
    //'completed', 'interactive', nil, others -> hide spinner
    [self.domCompletionListener invalidate];
    [self spinnerOff];
}

#10


-5  

The way I do it is this:

我这样做是这样的:

- (void)webViewDidFinishLoad:(UIWebView *)webview  {
    if (webview.loading)
    return;
    // now really done loading code goes next
    [logic]
}