1
votes

I'm migrating my app from UIWebView to WKWebView for iOS8.x (maintaining support for iOS7.x) users and I'm having a trouble with the website I'm displaying, because after rotating the iPad, it displays the content but the background seems somehow cropped and therefore there is a blank space after the bg.

However as soon as I start scrolling the website, it fixes automatically by itself. This blank effect also happens when I pass from landscape to portrait orientation, which leaves the right side of the website with the blank background.

Here's how I tryed solving the issue:

(void)willRotateToInterfaceOrientation:
(UIInterfaceOrientation)toInterfaceOrientation duration (NSTimeInterval)duration
{
    [self.view setNeedsUpdateConstraints];
    [_webView setNeedsUpdateConstraints];
}

(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [self.view setNeedsUpdateConstraints];
    [_webView setNeedsUpdateConstraints];
}

I've tried many things (reloading the background via JS, handling the willRotateToInterfaceOrientation and didRotateFromInterfaceOrientation methods as shown above) but have not been able to solve the issue, and it only happens when using WKWebView, if I switch back to UIWebView, the website renders properly.

I also tested the website on Safari for the very same device (an iPad2 with iOS 8.1.3) and it doesn't show the error, no matter how many times I rotate the iPad, so I'm sure it can be solved =). Can anybody please help me?

5

5 Answers

2
votes

This is a bug in WKWebKit that's usually triggered by HTML elements with position: fixed applied. The only workaround I've found is to use window.scrollTo() to scroll the page by one pixel a few hundred miliseconds after a resize event.

2
votes

This save my life:

self.wkWebView.evaluateJavaScript("window.scrollBy(1, 1);window.scrollBy(-1, -1);", completionHandler: nil)

Add this line on wkwebview did finish navigation event!!

1
votes

Took me a while, but I got it working for pages without scroll:

window.addEventListener('resize', function(){
            setTimeout(function () {
                var vp = document.getElementsByName("viewport")[0],
                    vpContent = vp.content,
                    vpHack = ((window.innerHeight > window.innerWidth) ? window.screen.availHeight  / window.innerHeight : window.screen.availWidth / window.innerWidth) + 0.0001;
                vp.content = "user-scalable=no, initial-scale="+vpHack+", maximum-scale="+vpHack+", minimum-scale="+vpHack+",width=device-width";
                setTimeout(function(){vp.content=vpContent;},10);
            }, 500);
        }, true);

That's assuming you use viewport. it changes the viewport by 0.0001 for 10 milliseconds

I implemented it like this:

-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
NSLog(@"###### FINISH");
[self.webView evaluateJavaScript:[NSString stringWithFormat:@"if (document.readyState==='complete'){ window.addEventListener('resize', function(){setTimeout(function () {var vp = document.getElementsByName('viewport')[0],vpContent = vp.content,vpHack = ((window.innerHeight > window.innerWidth) ? window.screen.availHeight  / window.innerHeight : window.screen.availWidth / window.innerWidth) + 0.0001; vp.content = 'user-scalable=no, initial-scale='+vpHack+', maximum-scale='+vpHack+', minimum-scale='+vpHack+',width=device-width';setTimeout(function(){vp.content=vpContent;},10);}, 500);}, true); } else { window.addEventListener('load', function(){window.addEventListener('resize', function(){setTimeout(function () {var vp = document.getElementsByName('viewport')[0],vpContent = vp.content,vpHack = ((window.innerHeight > window.innerWidth) ? window.screen.availHeight  / window.innerHeight : window.screen.availWidth / window.innerWidth) + 0.0001; vp.content = 'user-scalable=no, initial-scale='+vpHack+', maximum-scale='+vpHack+', minimum-scale='+vpHack+',width=device-width';setTimeout(function(){vp.content=vpContent;},10);}, 500);}, true);}, false );}"] completionHandler:nil];

}

0
votes

I use document.head.appendChild(document.createElement('style')).remove(); to tickle my WKWebViews into redrawing after resize/rotate.

0
votes

The following workaround worked for me, but unfortunately only on the Simulator:

- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
    [super didRotateFromInterfaceOrientation:fromInterfaceOrientation];

    // Workaround for WKWebView iOS 8 Rotation Bug
    // Official Fix (iOS 9): http://trac.webkit.org/changeset/169625
    if ([[[UIDevice currentDevice] systemVersion] integerValue] == 8) {
        CGRect frame = self.view.frame;
        frame.size.width += 1;
        self.view.frame = frame;
        frame.size.width -= 1;
        self.view.frame = frame;
    }
}

After some experimenting I got it to work on the device too using the following steps:

  • Keep two sets of AutoLayout constraints, the first one for the behavior you actually intend and the second with a small change (e.g. 1 point size difference)
  • Whenever the orientation changes, apply the second set to the view, then, after 1 second, apply the first constraints set.

While this solution is far from ideal, it is the only one I found without manipulating the Javascript on the website. I tried to lower the delay, but in my tests I had to wait for at least 0.8 seconds.