How to add buttons to wkWebView?

3.5k Views Asked by At

Because we make wkWebView as the root view, the button I added on my layout doesn't appear on screen. Then I tried to add wkWebView as a subview of the root view, but the app crashes. Then I found about UIBarButtonItem, but it seems that you could only create 2 buttons.

I need to add 5 buttons anywhere on the screen. Is there a workaround for this? I'm a newbie on ios development and I'm not that good at using the interface builder.

After trying @Matic Oblak's code and placing it on loadView() where I do my wkwebview initialization, I'm getting an error saying "Warning: Attempt to present SomeViewController on WebViewController whose view is not in the window hierarchy!

This is my current code.

override func loadView() {
//        let contentController: WKUserContentController = WKUserContentController()
//        let config: WKWebViewConfiguration = WKWebViewConfiguration()
//
//        contentController.add(self, name: "callbackHandler")
//        config.userContentController = contentController
//
//        webView = WKWebView(
//            frame: UIScreen.main.bounds,
//            configuration: config
//        )

       //webView.navigationDelegate = self
       //webView.translatesAutoresizingMaskIntoConstraints = false
       //webView.scrollView.isScrollEnabled = false

       //view = webView

       if let webViewContainer = webviewContainer {

           // Initialize:
           let contentController: WKUserContentController = WKUserContentController()
           let config: WKWebViewConfiguration = WKWebViewConfiguration()

           contentController.add(self, name: "callbackHandler")
           config.userContentController = contentController

           let webView = WKWebView(frame: webViewContainer.bounds, configuration: WKWebViewConfiguration()) // Create a new web view
           webView.translatesAutoresizingMaskIntoConstraints = false // This needs to be called due to manually adding constraints

           webView.navigationDelegate = self
           webView.translatesAutoresizingMaskIntoConstraints = false
           webView.scrollView.isScrollEnabled = false

           // Add as a subview:
           webViewContainer.addSubview(webView)

           // Add constraints to be the same as webViewContainer
           webViewContainer.addConstraint(NSLayoutConstraint(item: webView, attribute: .leading, relatedBy: .equal, toItem: webViewContainer, attribute: .leading, multiplier: 1.0, constant: 0.0))
           webViewContainer.addConstraint(NSLayoutConstraint(item: webView, attribute: .trailing, relatedBy: .equal, toItem: webViewContainer, attribute: .trailing, multiplier: 1.0, constant: 0.0))
           webViewContainer.addConstraint(NSLayoutConstraint(item: webView, attribute: .top, relatedBy: .equal, toItem: webViewContainer, attribute: .top, multiplier: 1.0, constant: 0.0))
           webViewContainer.addConstraint(NSLayoutConstraint(item: webView, attribute: .bottom, relatedBy: .equal, toItem: webViewContainer, attribute: .bottom, multiplier: 1.0, constant: 0.0))

           // Assign web view for reference
           self.webView = webView
       }
   }

override func viewDidLoad() {
    if(someVar1.isEmpty && someVar2.isEmpty){
           showSomeDialog()
       }
}

The first commented lines on loadView() are my previous code for reference.

After placing @Matic Oblak's code on viewDidLoad(), I'm getting a black screen. Also, after placing the code that calls SomeViewController on viewDidAppear(), the error is gone but I'm still getting the black screen.

2

There are 2 best solutions below

6
Matic Oblak On BEST ANSWER

Actually WKWebView has a bit of a nasty bug that it crashes when added in storyboard. But you can do that in code. What I usually do is create a view in storyboard just as a web view panel (usually full screen but not if you have some tool bars) then just add a new web view as a subview. I still use constraints to add the web view:

import UIKit
import WebKit

class WebViewController: UIViewController {

    @IBOutlet private var webViewContainer: UIView? // A view on which a web view will be added to
    private var webView: WKWebView? // Web view manually added

    override func viewDidLoad() {
        super.viewDidLoad()

        if let webViewContainer = webViewContainer {

            // Initialize:
            let webView = WKWebView(frame: webViewContainer.bounds, configuration: WKWebViewConfiguration()) // Create a new web view
            webView.translatesAutoresizingMaskIntoConstraints = false // This needs to be called due to manually adding constraints

            // Add as a subview:
            webViewContainer.addSubview(webView)

            // Add constraints to be the same as webViewContainer
            webViewContainer.addConstraint(NSLayoutConstraint(item: webView, attribute: .leading, relatedBy: .equal, toItem: webViewContainer, attribute: .leading, multiplier: 1.0, constant: 0.0))
            webViewContainer.addConstraint(NSLayoutConstraint(item: webView, attribute: .trailing, relatedBy: .equal, toItem: webViewContainer, attribute: .trailing, multiplier: 1.0, constant: 0.0))
            webViewContainer.addConstraint(NSLayoutConstraint(item: webView, attribute: .top, relatedBy: .equal, toItem: webViewContainer, attribute: .top, multiplier: 1.0, constant: 0.0))
            webViewContainer.addConstraint(NSLayoutConstraint(item: webView, attribute: .bottom, relatedBy: .equal, toItem: webViewContainer, attribute: .bottom, multiplier: 1.0, constant: 0.0))

            // Assign web view for reference
            self.webView = webView
        }
    }

}

So here webViewContainer is dragged from storyboard as any view you want. It can even be the main view of the view controller if you want to.

The rest should be explained in the comments.

So using this procedure you don't need to "hack" adding subviews on top of everything. You can add your button normally in your storyboard.

Note: The problem with using webViewContainer as main view is that web view will be added later than the rest of views so it may cover those you added in storyboard. You need to either send the web view to back or bring overlaying items to front. It is just adding webViewContainer.sendSubviewToBack(webView) but I still prefer to use a separate view just for web view.

2
Sadman Samee On

You could add floating button on top of your viewcontroller by using some third party library. Let's say using Floaty or with any other similar.