9

My UIWebview loads a local html file using loadHtmlString. The loaded page has links to other local html files as well as real links that use the internet.

I have added a back button with:

if ([self.webView canGoBack]) [self.webView goBack];

This works fine except it does not recognise the original page loaded with loadHtmlString.

For example, if I navigate:
local -> local -> web
local X local <- web (The first back works, the next does nothing.)

How can I get the webview to recognise the original page so the back button also works for it? Can I add it to the webview's history somehow?

Thanks in advance!

DenVog
  • 4,226
  • 3
  • 43
  • 72
Felix
  • 3,783
  • 5
  • 34
  • 53
  • I would argue that `loadHtmlString:` is bad practice for all but the simplest use cases. You should consider running a local web server instead. I've had success with [CocoaHTTPServer](https://github.com/robbiehanson/CocoaHTTPServer) and [Mustache templates](https://github.com/groue/GRMustache). If you're able to write your entire app in HTML/JavaScript, consider using [Trigger.io](https://trigger.io/) or similar. – Neal Ehardt Jan 29 '15 at 23:03
  • My use case was pretty simple. Simply serving static pages as part of a book like format. – Felix Jan 30 '15 at 02:53
  • Static pages? Great, save them to your bundle and use the technique I describe here http://stackoverflow.com/a/17557309/354144 – Neal Ehardt Feb 02 '15 at 23:53
  • Cheers Neal, good to know! – Felix Feb 03 '15 at 02:18

4 Answers4

12

I had a same problem. I tried manage the history, but it is error prone. Now I have discovered a better solution of this.

What you want to do is simply add a loadRequest to about:blank and make that as a placeholder for you before you call loadHTMLString/loadData. Then you are totally free from monitoring the history. The webview.canGoBack and canGoForward will just work. Of course, you will need a hack to handle go back to the placeholder about:blank. You can do that in webViewDidFinishLoad. Here is the code highlight:

In the function when you call loadHTMLString:

[weakSelf.fbWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]];
[weakSelf.fbWebView loadHTMLString:stringResponse baseURL:url];

Code to handle goBack:

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
  if ([webView.request.URL.absoluteString isEqualToString:@"about:blank"]
      && ![webView canGoBack] && [webView canGoForward]) {
    [weakSelf.fbWebView loadHTMLString:stringResponse baseURL:url];
  }
}

I think it is also possible expand this solution to handle those loadHTMLString that is not the first load. Just by having a Stack to record all the string response and insert an about:blank on each loadHTMLString. And pop the stack when each time go back to about:blank.

Cloud Xu
  • 3,267
  • 2
  • 15
  • 14
7

as Cloud Xu said you just need to preload dummy html, like this:

    webView.load(URLRequest(url: URL(string: "about:blank")!))
    webView.loadHTMLString("<html><body><b>Your page data</b></body></html>", baseURL: nil)

just it and nothing else! Swift3

Nosov Pavel
  • 1,571
  • 1
  • 18
  • 34
2

Here's the workaround I ended up using:

I created a delegate for the WebView, which kept track of whether the user was on the first page (loaded using loadHtmlString).

When the back button is pressed, and the WebView can no longer go back, and the user is NOT on the first page, the first page is loaded again (using loadHtmlString). Here is the relevant code:

if ([self.webView canGoBack])
    [self.webView goBack];
else
{
    WebViewDelegate * customDel = (WebViewDelegate *) self.webView.delegate;
    if (customDel.onFirstPage == NO)
    {
        // Load original local page.
        [self.webView loadHTMLString:self.htmlStr baseURL:[[NSBundle mainBundle] bundleURL]];

        customDel.onFirstPage = YES;
    }
}

I'll not choose this answer for a little while, in case a more elegant solution pops up.

Hopefully this helps someone!

Felix
  • 3,783
  • 5
  • 34
  • 53
  • 1
    what if user open your html page, click a link to another URL, then how can you toggle **onFirstPage** ? – onmyway133 Dec 24 '13 at 04:26
  • I think it has a bug. Say you landed on site A with loadHTMLString. Then go to B, Then go to C. Then back to B, and back to A. Then if you goForward, it will go straight to C. B is missing – Cloud Xu Jan 29 '15 at 22:53
0

You can also load just a Data URL with the HTML content you want to display, and avoid making an empty load and then loading the content separately.

The format is: data:text/html,[HTML string]

Ps. The HTML string must be percent encoded (URL encoded).

func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
    let htmlString = """
<html>
    <body>
        hello world
    </body>
</html>
"""
    if let url = URL(string: "data:text/html," + (htmlString.addingPercentEncoding(withAllowedCharacters: []) ?? "")) {
        webView.load(.init(url: url))
    }
}
vicegax
  • 4,709
  • 28
  • 37