How does iOS 6 UIWebView state restoration work?
Asked Answered
F

2

6

According the docs:

In iOS 6 and later, if you assign a value to this view’s restorationIdentifier property, it attempts to preserve its URL history, the scaling and scrolling positions for each page, and information about which page is currently being viewed. During restoration, the view restores these values so that the web content appears just as it did before.

I'm doing all that, but nothing is happening. And even if I manually save and restore the URL the user was looking at previously, the scroll position is not restored. Are the docs just wrong?

Falcone answered 29/1, 2013 at 16:52 Comment(0)
K
5

I have tried Matt's answer and it works well, however if there are 'forward' pages in the history stack-they will be replaced by the restored request within the web view object.

A better approach is to instead call the 'reload' method on the webview object, this will restore the history in both directions as well as the zoom and content scroll offset.

If you'd like an example of my approach or some more functionality for your own webview, take a look at my open source fork of SVWebViewController here.

Kuhl answered 26/6, 2013 at 17:34 Comment(2)
That's better than my answer!Falcone
Everyone should read Matt's answer to understand what's going on and where the code show go. Just replace his "loadRequest" call with "reload".Thermotherapy
F
12

The docs are right, but very incomplete. Here's what's going on. If a web view participates in state restoration (I'm assuming you know what this means - everything has to have a restorationIdentifier, and so on), and if the web view had a request (not an HTML string) when the user left the app, the web view will automatically return to life containing the same request as its request property, and with its Back and Forward lists intact. Thus, you can use the state restoration mechanism to restore the state of the web view, but you have to perform a little extra dance. This dance is so curious and obscure that initially I was under the impression that a web view's state couldn't really be saved and restored, despite the documentation's assertion that it could.

There are two secrets here; once you know them, you'll understand web view state restoration:

  • A restored web view will not automatically load its request; that's up to your code.

  • After a restored web view has loaded its request, the first item in its Back list is the same page in the state the user left it (scroll and zoom).

Knowing this, you can easily devise a strategy for web view state restoration. The first thing is to detect that we are restoring state, and raise a flag that says so:

-(void)decodeRestorableStateWithCoder:(NSCoder *)coder {
    [super decodeRestorableStateWithCoder:coder];
    self->_didDecode = YES;
}

Now we can detect (perhaps in viewDidAppear:) that we are restoring state, and that the web view magically contains a request, and load that request:

if (self->_didDecode && wv.request)
    [wv loadRequest:wv.request];

Now for the tricky part. After the view loads, we immediately "go back." This actually has the effect of restoring the user's previous scroll position (and of removing the extra entry from the top of the Back stack). Then we lower our flag so that we don't make this extra move at any other time:

- (void)webViewDidFinishLoad:(UIWebView *)wv {
    if (self->_didDecode && wv.canGoBack)
        [wv goBack];
    self->_didDecode = NO;
}

The UIWebView is now in the state it was in when the user previously left the app. We have saved and restored state using the built-in iOS 6 state saving and restoration feature.

Falcone answered 29/1, 2013 at 16:54 Comment(7)
I don't know if it's a quirk of web views, but after [wv goBack], testing [wv canGoBack] still returns YES, again and again. Tried it on a web view with only one page in the history (iOS 6.1.4).Protestant
Is there a project anywhere with this working? Can't get it to workLeto
Out of curiosity, how does one go about crafting a great question to a complex, puzzling problem, and then promptly obliterate it with a comprehensive self-answer, in great detail, all within the span of 2 minutes? Nice! Where's the SO badge for that trick?Banc
@GregCombs stackoverflow.com/help/self-answer "If you have a question that you already know the answer to, and you would like to document that knowledge in public so that others (including yourself) can find it later, it's perfectly okay to ask and answer your own question on a Stack Exchange site." - And of course what you're missing is the weeks of work on this issue that preceded the posting of the question-and-answer.Falcone
LOL, got it! I'm certainly happy you did!Banc
I know this is old, but you can also just call [webView reload] instead of forward and back again. (on iOS 7 anyway...) The scroll position is saved as well.Cinthiacintron
@Rolleric "But if the web view was loaded from an HTML string", sure, because in that case there is no URL request, which is what is saved for you. Did you notice that in my answer I said not an HTML string??? That's whyFalcone
K
5

I have tried Matt's answer and it works well, however if there are 'forward' pages in the history stack-they will be replaced by the restored request within the web view object.

A better approach is to instead call the 'reload' method on the webview object, this will restore the history in both directions as well as the zoom and content scroll offset.

If you'd like an example of my approach or some more functionality for your own webview, take a look at my open source fork of SVWebViewController here.

Kuhl answered 26/6, 2013 at 17:34 Comment(2)
That's better than my answer!Falcone
Everyone should read Matt's answer to understand what's going on and where the code show go. Just replace his "loadRequest" call with "reload".Thermotherapy

© 2022 - 2024 — McMap. All rights reserved.