Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does iOS 6 UIWebView state restoration work?

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?

like image 701
matt Avatar asked Jan 29 '13 16:01

matt


2 Answers

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.

like image 135
matt Avatar answered Oct 16 '22 09:10

matt


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.

like image 43
Pellet Avatar answered Oct 16 '22 10:10

Pellet