WKWebView added as Subview is not resized on rotation in Swift

14,745

Solution 1

I managed to solve the problem by using this line of code:

self._webView!.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight

:)

Solution 2

swift 3 version:

webView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]

Solution 3

I'm posting an answer for Objective-C, just in case if someone comes here looking for it

[self.webView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
Share:
14,745

Related videos on Youtube

PastaCoder
Author by

PastaCoder

Updated on September 27, 2022

Comments

  • PastaCoder
    PastaCoder over 1 year

    I'm working on adding a new reading view to my browser app. It is another view controller, that only includes a WKWebView added as a subview with a button (and gesture) to close the view. Everything works great, but when I rotate the device, the subview isn't resized, so I have one half of the screen empty.

    The WKWebView in the Reading View gets the URL of the main View Controller with a segue performed after the user taps a button on the main View Controller and that URL is stored as webpageURL.

    Here is the code I used:

    import UIKit
    import WebKit
    
    class ReadingViewController: UIViewController, UIGestureRecognizerDelegate, WKNavigationDelegate, WKScriptMessageHandler {
    
    @IBOutlet weak var _closeButton: UIButton!
    @IBOutlet weak var _progressView: UIProgressView!
    @IBOutlet weak var _loadingErrorView: UIView!
    
    var webpageURL: NSURL?
    
    var _webView: WKWebView?
    var _isMainFrameNavigationAction: Bool?
    var _loadingTimer: NSTimer?
    
    var _swipeFromTopRecognizer: UIScreenEdgePanGestureRecognizer?
    var _panFromRightRecognizer: UIScreenEdgePanGestureRecognizer?
    var _panFromLeftRecognizer: UIScreenEdgePanGestureRecognizer?
    var _errorView: UIView?
    var _isCurrentPageLoaded = false
    
    var _progressTimer: NSTimer?
    var _isWebViewLoading = false
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        var contentController = WKUserContentController();
        var scaleToFit = WKUserScript(source: "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);", injectionTime: WKUserScriptInjectionTime.AtDocumentStart, forMainFrameOnly: true)
        contentController.addUserScript(scaleToFit)
        contentController.addScriptMessageHandler(self, name: "callbackHandler")
    
        var webViewConfiguration: WKWebViewConfiguration = WKWebViewConfiguration()
        webViewConfiguration.allowsInlineMediaPlayback = true
        webViewConfiguration.mediaPlaybackRequiresUserAction = false
    
        _webView = WKWebView(frame: self.view.frame, configuration: webViewConfiguration)
    
        self.view.addSubview(_webView!)
        _webView!.navigationDelegate = self
        self.view.sendSubviewToBack(_webView!)
        _webView!.allowsBackForwardNavigationGestures = true
    
        _loadingErrorView.hidden = true
    
        _swipeFromTopRecognizer = UIScreenEdgePanGestureRecognizer(target: self, action: Selector("handleSwipeFromTop:"))
        _swipeFromTopRecognizer!.edges = UIRectEdge.Top
        _swipeFromTopRecognizer!.delegate = self
        self.view.addGestureRecognizer(_swipeFromTopRecognizer!)
    
        _progressView.hidden = true
    
        var urlAsString = "\(webpageURL!)"
        loadURL(urlAsString)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    // UI Control Functions
    
    func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
    
    @IBAction func closeReadingView(sender: AnyObject) {
        self.dismissViewControllerAnimated(true, completion: nil)
    }
    
    func closeButtonEnabled(bool:Bool) {
        _closeButton.enabled = bool
    }
    
    func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
        if(message.name == "callbackHandler") {
            println("JavaScript is sending a message \(message.body)")
        }
    }
    
    
    // WebView Functions
    
    func webView(webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        _loadingErrorView.hidden = true
        _isWebViewLoading = true
        _progressView.hidden = false
        _progressView.progress = 0
        _progressTimer = NSTimer.scheduledTimerWithTimeInterval(0.01667, target: self, selector: "progressTimerCallback", userInfo: nil, repeats: true)
        _loadingTimer = NSTimer.scheduledTimerWithTimeInterval(30, target: self, selector: "loadingTimeoutCallback", userInfo: nil, repeats: false)
    }
    
    func loadingTimeoutCallback() {
        _webView?.stopLoading()
        handleWebViewError()
    }
    
    func webView(webView: WKWebView, didCommitNavigation navigation: WKNavigation!) {
        _isCurrentPageLoaded = true
        _loadingTimer!.invalidate()
        _isWebViewLoading = false
    
        if self._webView!.URL == webpageURL! {
            handleWebViewError()
            println(webpageURL!)
            println(self._webView!.URL!)
        } else {
            println("Page was loaded successfully")
            println(webpageURL!)
            println(self._webView!.URL!)
        }
    }
    
    func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
        _isCurrentPageLoaded = true
        _loadingTimer!.invalidate()
        _isWebViewLoading = false
    }
    
    func webView(webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: NSError) {
        if let newFrameLoading = _isMainFrameNavigationAction {
            } else {
            handleWebViewError()
        }
    }
    
    func webView(webView: WKWebView, didFailNavigation navigation: WKNavigation!, withError error: NSError) {
        if let newFrameLoading = _isMainFrameNavigationAction {
            } else {
            handleWebViewError()
        }
    }
    
    func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {
        if (navigationAction.targetFrame == nil && navigationAction.navigationType == .LinkActivated) {
            _webView!.loadRequest(navigationAction.request)
        }
        _isMainFrameNavigationAction = navigationAction.targetFrame?.mainFrame
        decisionHandler(.Allow)
    }
    
    func handleWebViewError() {
        _loadingTimer!.invalidate()
        _isCurrentPageLoaded = false
        _isWebViewLoading = false
        displayLoadingErrorMessage()
    }
    
    func progressTimerCallback() {
        if (!_isWebViewLoading) {
            if (_progressView.progress >= 1) {
                _progressView.hidden = true
                _progressTimer?.invalidate()
            } else {
                _progressView.progress += 0.2
            }
        } else {
            _progressView.progress += 0.003
            if (_progressView.progress >= 0.95) {
                _progressView.progress = 0.95
            }
        }
    }
    
    func loadURL(urlString: String) {
        let addrStr = httpifyString(urlString)
        let readingAddr = addrStr.stringByAddingPercentEncodingForFormUrlencoded()!
        let addr = NSURL(string: "http://mobilizer.instapaper.com/m?u=\(readingAddr)")
        if let webAddr = addr {
            let req = NSURLRequest(URL: webAddr)
            _webView!.loadRequest(req)
        } else {
            displayLoadingErrorMessage()
        }
    
    }
    
    func httpifyString(str: String) -> String {
        let lcStr:String = (str as NSString).lowercaseString
        if (count(lcStr) >= 7) {
            if (lcStr.rangeOfString("http://") != nil) {
                return str
            } else if (lcStr.rangeOfString("https://") != nil) {
                return str
            }
        }
        return "http://"+str
    }
    
    
    func displayLoadingErrorMessage() {
        _loadingErrorView.hidden = false
    }
    
    func handleGoBackPan(sender: UIScreenEdgePanGestureRecognizer) {
        if (sender.state == .Ended) {
                _webView!.goBack()
        }
    }
    
    func handleGoForwardPan(sender: AnyObject) {
        if (sender.state == .Ended) {
                _webView!.goForward()
        }
    }
    
    func handleSwipeFromTop(sender: AnyObject) {
        self.dismissViewControllerAnimated(true, completion: nil)
    }
    
    override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
        coordinator.animateAlongsideTransition({ context in
            self._webView!.frame = CGRectMake(0, 0, size.width, size.height)
        }, completion: nil)
    }
    

    }

    And here are some screenshots to demonstrate the issue: This is the view after it finished loading, working correctly: This is how the view loads, working correctly

    This is the view after rotating the device to landscape: This is the view after rotation

    And this is the scroll location after rotation: Scroll location after rotation

    Using self.view = _webView makes the view resize correctly, but ignores all the views on the Storyboard (since the View's contents are being rewritten).

    How can I fix this issue (without rewriting self.view)?

  • Joel
    Joel almost 9 years
    I'd up vote this more than once if I could! Thanks!
  • Mike Cole
    Mike Cole over 8 years
    New syntax would be webView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight].
  • tech4242
    tech4242 almost 8 years
    This is a life saver! Using NSLayoutConstraints with WKWebView was not the best experience to say the least
  • Oliver Zhang
    Oliver Zhang over 7 years
    Thanks a lot! This is fantastic!
  • computingfreak
    computingfreak about 7 years
    mind the case, webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  • Itachi
    Itachi over 6 years
    What's the replacement of layout constraints?
  • CupawnTae
    CupawnTae about 5 years
    or of course self.webView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
  • Gleno
    Gleno almost 2 years
    Newest syntax would be: self.webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]