KleMiX
KleMiX

Reputation: 332

WKWebView incorrect content width in landscape with safe area

This is how it looks: wrong offsets and width Maximally simple code to reproduce:

#import "ViewController.h"
#import <WebKit/WebKit.h>

@implementation ViewController

- (void)loadView
{
    WKWebView* webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:[[WKWebViewConfiguration alloc] init]];
    self.view = webView;

    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://apple.com"]]];
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscape;
}

@end

This happens only to the websites with viewport-fit=cover set. If you open same websites in Safari, you can see that first draw frame has the same problem, but it instantly resizes correctly after.

This happens only when launched in landscape. Rotation to portrait and back to landscape fixes this. Even minimizing app and opening it again fixes this.

So is there anything I can do from code to force it fix itself? I've already tried calling setNeedsLayout and layoutIfNeeded to whole WKWebView tree. Setting scroller insets will make content look like it doesn't have viewport-fit=cover set. This happens on iOS 12 and 13 (tested) and iOS 13 Simulator.

Upvotes: 3

Views: 2331

Answers (3)

Mel
Mel

Reputation: 959

I ran into this same issue but I believe I found a simpler solution that doesn't involve deactivating and reactivating constraints.

Instead, call setNeedsLayout() from the didFinish navigation delegate method. This triggers another layout pass after the web view has finished loading:

override func viewDidLoad() {
    super.viewDidLoad()

    mainWebView.navigationDelegate = self
    mainWebView.load(URLRequest(url: URL(string: "https://apple.com")!))
}

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    webView.setNeedsLayout()
}

To ensure you're reliably reproducing the issue, you can slow the simulator animation to ensure the web view loads before the view controller finishes animating on screen.

Thank you @Stefan for pointing me in the right direction!

Upvotes: 0

sDev
sDev

Reputation: 1471

Hi, You need to change viewport.

Please change your "loadView" function with below code.

- (void)loadView
{
    NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";

    WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
    WKUserContentController *wkUController = [[WKUserContentController alloc] init];
    [wkUController addUserScript:wkUScript];

    WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init];
    wkWebConfig.userContentController = wkUController;

    WKWebView* webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:wkWebConfig];
    webView.insetsLayoutMarginsFromSafeArea = false;
    webView.scrollView.insetsLayoutMarginsFromSafeArea = false;
    webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    webView.translatesAutoresizingMaskIntoConstraints = NO;
    self.view = webView;

    UIWindow *window = UIApplication.sharedApplication.windows.firstObject;
    CGFloat topPadding = window.safeAreaInsets.top;
    CGFloat bottomPadding = window.safeAreaInsets.bottom;
    CGFloat leftPadding = window.safeAreaInsets.left;
    CGFloat rightPadding = window.safeAreaInsets.right;
    self.additionalSafeAreaInsets =  UIEdgeInsetsMake(-topPadding, -leftPadding, -bottomPadding, -rightPadding);
    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://apple.com"]]];
}

Upvotes: 0

Stefan
Stefan

Reputation: 166

It is not the perfect solution but I managed to fix this by adding the webView as subView to self.view and add constraints to superview instead of safeArea to get the same effect.

Solution:

  • Connect trailing constraint to a variable
  • Set constraint isEnabled to false in viewDidLoad (to optimize check for deviceOrientation)
  • In function webView(WKWebView, didFinish: WKNavigation) set constraint isEnabled to true
  • Don't forget to register to navigationDelegate of the webView

    @IBOutlet var mainWebView: WKWebView!
    @IBOutlet var trailingConstraint: NSLayoutConstraint!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        let orientation = UIDevice.current.orientation
        if orientation == .landscapeLeft || orientation == .landscapeRight {
            trailingConstraint.isActive = false
        }
    
        mainWebView.navigationDelegate = self
        mainWebView.load(URLRequest(url: URL(string: "https://apple.com")!))
    }
    
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        trailingConstraint.isActive = true
    }
    

Upvotes: 2

Related Questions