UIWebView: when did a page really finish loading?

wkwebview did finish loading swift
wkwebview objective-c
wkwebview loading indicator objective-c
ios webview example
wkwebview inject javascript
uiwebviewdelegate
wknavigation

I need to know, when a web page has completely been loaded by UIWebView. I mean, really completely, when all redirects are done and dynamically loaded content is ready. I tried injecting javascript (querying for document.readyState == 'complete'), but that does not seem to be very reliable.

Is there, maybe, an event from the private api that will bring me the result?

I have been looking for the answer for this, and I got this idea from Sebastian's question. Somehow it works for me, maybe it will for those who encounter this issue.

I set a delegate to UIWebView and in the webViewDidFinishLoad, I detect if the webview has really finished loading by executing a Javascript.

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

UIWebView: when did a page really finish loading?, I set a delegate to UIWebView and in the webViewDidFinishLoad, I detect if the webview has really finished loading by executing a Javascript. - (void)  Thanks for the very helpful answer. Bug filed here: rdar://27892687 As noted in the bug, WKWebView appears to silently fail in this scenario, while UIWebView errors.

My solution:

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

Learn iOS 8 App Development, Looking up the UIWebViewDelegate protocol, you find that it lists four webViewDidFinishLoad(_:) is called when it's finished. a web page begins to load, this function will disable (by setting the enabled property to After a web page loads, this line digs into the webView object to find the URL that was actually loaded. 'dom-ready' and 'did-finish-load' events were fired twice for the unreachable url in the webview. #2666

UIWebView's ability to execute Javascript is independent of whether a page has loaded or not. Its possible use stringByEvaluatingJavaScriptFromString: to execute Javascript before you have even made a call to load a page in UIWebView in the first place, or without loading a page at all.

Therefore I cannot understand how the answer in this link is accepted: webViewDidFinishLoad: Firing too soon? because calling any Javascript doesn't tell you anything at all (if they are calling some interesting Javascript, which is monitoring the dom for example, they don't make any mention of that and if there were they would because its important).

You could call some Javascript that, for example, examines the state of the dom or the state of the loaded page, and reports that back to the calling code however if it reports that the page has not loaded yet then what do you do? - you'll have to call it again a bit later, but how much later, when, where, how, how often, .... Polling is usually never a nice solution for anything.

The only way to know when the page has totally loaded and be accurate and in control of knowing exactly what what its in is to do it yourself - attach JavaScript event listeners into the page being loaded and get them to call your shouldStartLoadWithRequest: with some proprietary url. You could, for example, create a JS function to listen for a window load or dom ready event etc. depending upon if you need to know when the page has loaded, or if just the dom had loaded etc. Depending upon your needs.

If the web page is not your's then you can put this javascript into a file and inject it into every page you load.

How to create the javascript event listeners is standard javascript, nothing specially to do with iOS, for example here is the JavaScript to detect when the dom has loaded in a form that can be injected into the loading page from within the UIWebView and then result in a call to shouldStartLoadWithRequest: (this would invoke shouldStartLoadWithRequestwhen: the dom has finished loadeding, which is before the full page content has been displayed, to detect this just change the event listener).

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();

IOS 8 for Programmers: An App-driven Approach with Swift, When the web page starts loading, we display a network activity indicator in the status (lines 40–43) is called when a UIWebView finishes loading a URL. if there's no network connection when a request is made, the request will fail and an  Migrating from UIWebView / WebView to WKWebView WKWeb View has been the preferred API since iOS 8. But if your app still hasn’t made the switch, be advised that UIWeb View and Web View are formally deprecated in iOS 12 and macOS Mojave , and you should update to WKWeb View as soon as possible.

Martin H's answer is on the right track, but it can still get fired multiple times.

What you need to do is add a Javascript onLoad listener to the page and keep track of whether you've already inserted the listener:

#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;
}

When the page is finished loading, the Javascript listener will trigger a new page load of the URL pageloaded:<URL that actually finished>. Then you just need to update shouldStartLoadWithRequest to look for that URL scheme:

- (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;
    }
}

The Complete Idiot's Guide to Ipad and Iphone App Development, While the web page is loading, you will display a It might sound like a lot, but the code to build the sample app is simple, as you will find out very shortly. interface and use the UIWebViewDelegate to know when it finishes loading a URL. But whatever the time is, if the page actually takes longer to load than that set time, it will never finish loading before it starts loading over again. If you have a slow internet connection, for example, that could be at play for pages that do this. Another scenario are pages that we now call “infinite scroll”.

You could also use the accepted answer here: UIWebView - How to identify the "last" webViewDidFinishLoad message?...

Basically, use the estimatedProgress property and when that reaches 100, the page has finished loading... See this guide for help.

Although it does use a private framework, according to the guide there are some apps in the App Store that use it...

Cocoa Touch for iPhone OS 3, To add the UIWebView to your application, simply create a new view and its accompanying withyour UIWebView attached toit, you can ask it to actually load content. Listing 16.2 Loading aWeb Page intoaUIWebView -(void)​viewDidLoad to be notified when the page is finished loading so that you can update your title. Use the stopLoading () method to stop loading, and the isLoading property to find out if a web view is in the process of loading. Set the delegate property to an object conforming to the WKUIDelegate protocol to track the loading of web content. See Listing 1 for an example of creating a WKWebView programmatically. Listing 1

IPhone SDK 3, (In fact, Safari on the iPhone uses a UIWebView for display.) Tapping links can load pages, and tapping in text controls will open a keyboard for data entry. webViewDidFinishLoad: is sent when the web view finishes loading a page and is  The methods of the WKNavigation Delegate protocol help you implement custom behaviors that are triggered during a web view's process of accepting, loading, and completing a navigation request. SDKs iOS 8.0+

webViewDidFinishLoad(_:), Declaration. optional func webViewDidFinishLoad(_ webView: UIWebView). Parameters. webView. The web view has finished loading. UIWebView just draws what it already has (which is nothing) instead of waiting for rendering to finish, and UITableView just displays the unaltered old contents. Did I understand correctly? Is there any fix for this? Can I force the UIWebView to render immediately? Or sould I just give up on UIWebView and draw the text manually in a custom draw

webView(_:didFinish:), Called when the navigation is complete. On This Page optional func webView(_ webView: WKWebView, didFinish navigation: Tracking Load Progress. UIWebView has a delegate method which is called as soon as the web page loading operation is completed. Make sure you modify DOM after web page loading is completed. If you try to do it before it, it will be overridden by default rule in specified on the web page.

Comments
  • I don't think that you need a private API. See my answer below...
  • I also added an answer that does use a private framework, but still may not get you rejected from the App Store. In any case, I would recommend using the estimatedProgress method only if the webViewDidFinishLoad method doesn't work...
  • You said you tried injecting javascript but it didn't seem reliable. In my experience it is, where/how are you injecting the code, when are you calling that code, when and how are you calling document.readyState?
  • The only solution (I found) which works ok: stackoverflow.com/questions/1662565/…
  • If your answer works, it seems the best answer I've seen all over stack overflow for this. But no one has voted for this answer in the 3 years its been here, so I suspicious... I will give it a shot.
  • Great! But don't forget to add UIWebViewDelegate in the header, and [webViewImage setDelegate:self] in the implementation file where you define the UIWebView
  • This didn't work for me when trying to load the following URL: imgur.com/A9dlJpQ
  • For all six 'OnPageFinished' calls, the state was complete. So it did not work for me as a method to detect the last pageload event.
  • To my understanding, the webViewDidFinishLoad only gets called once for each page load. So if the document.readyState is not "complete" when UIWebView thinks the load is complete. This delegate method won't ever be called again. So essentially we are getting dead code in this if block in this case.
  • This seems to work really well for me. Why all the complicated solutions? Am I missing something?