28

With iOS 11 Apple has added the ability set add WKWebViews outlets on your nibs and storyboards. It seems to work fine when using the default WKWebViewConfiguration that get set automatically.

However, I'd like to be able to use a custom WKWebViewConfiguration. Is there anyway I can set this before, or after the WKWebView gets initialized from the nib?

Matt.M
  • 1,039
  • 3
  • 14
  • 21

6 Answers6

28

Let's Example your customize configuration.

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];

//Here you can customize configuration
[self.webView.configuration.userContentController addUserScript:wkUScript];

// self.webView.navigationDelegate = self;
// self.webView.contentMode = UIViewContentModeScaleAspectFill;
Ravi Kumar
  • 1,356
  • 14
  • 22
  • 3
    This was it for me: Changing the `userContentController` to one that had the same script on it didn't do anything, but directly calling `addUserScript` on the existing `userContentController` worked like a charm. – DesignatedNerd Jan 24 '19 at 10:48
  • 6
    However this doesn't address the actual question that was asked since nowhere was a custom WKWebViewConfiguration added. I guess it was OK for the poster but I need to do something else that will not work with the technique of this answer. I'll open up a new question for that. – Locksleyu May 31 '19 at 20:19
13

You can create WKWebView in Storyboard and then use WKUIDelegate:createWebViewWith for creating a new WKWebView with configuration alone.

class WindowViewController: UIViewController {
    
    // The WKWebView created from storyboard
    @IBOutlet var webView: WKWebView!
    
    override func viewDidLoad() {

        // Delegate the webview's uiDelegate
        self.webView.uiDelegate = self
    }
}

extension WindowViewController: WKUIDelegate {
    
    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {

        // Create new WKWebView with custom configuration here
        let configuration = WKWebViewConfiguration()
        
        return WKWebView(frame: webView.frame, configuration: configuration)
    }
}
Angolao
  • 986
  • 1
  • 15
  • 27
  • didn't get a chance to test this but seems plausible. I'm wondering if this delegate method gets called on every request? – Matt.M Mar 20 '19 at 23:27
  • The Apple docs for this delegate method state "The web view returned must be created with the specified configuration." So even if this works, it seems like a bad idea. – Locksleyu May 31 '19 at 19:31
  • 6
    Also, I think this approach may not work since the createWebViewWith... will not be called on the initial WKWebView creation, only subsequent ones. The reason for this is that the initial one was already created by the time viewDidLoad() runs. – Locksleyu May 31 '19 at 20:13
  • 5
    Tried it myself. To save your time: this does NOT work! – us_david Jun 24 '22 at 16:27
10

It is not possible to set the configuration of the WKWebView through the outlet of WKWebView form the storyboard because the configuration property of the WKWebView is read only, instead we need to programmatically configure as given below.

class ViewController: UIViewController, WKScriptMessageHandler {

    @IBOutlet weak var webContentView: UIView!
    var webView: WKWebView?

    let contentController = WKUserContentController()        
    contentController.add(self, name: "callbackHandler")

    let configuration = WKWebViewConfiguration()
    configuration.userContentController = contentController

    self.webView = WKWebView(frame: self.webContentView.bounds, configuration: configuration)
    self.webContentView.addSubview(self.webView!)
}

And implement the WKScriptMessageHandler delegate method

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    if (message.name == "callbackHandler"){
        print("\(message.body)")
    }
}

Hope this helps...

Pierre F
  • 1,332
  • 15
  • 33
Ramprasad A
  • 221
  • 1
  • 13
9

Yes!!! We can set WKWebViewConfiguration on WKWebView from Nib or Storyboard I am using below code for that:

Here is my IBOutlet--

 @IBOutlet  var webViewChat: WKWebView!

//Using below code you can set configuration

 webViewChat.configuration.userContentController.add(self, name: "edit")
 webViewChat.configuration.userContentController.add(self, name: "remove")

Note : Make sure you load URL after setting up the configuration

guru
  • 2,727
  • 3
  • 27
  • 39
  • 1
    Yes it works, even though the [WKWebViewConfiguration Documentation](https://developer.apple.com/documentation/webkit/wkwebviewconfiguration) explicitly states that "WKWebViewConfiguration is only used when a web view is first initialized. You cannot use this class to change the web view's configuration after it has been created." – Pierre F Nov 26 '19 at 13:39
  • This is much simpler than the other answers. Thank you! – Clifton Labrum Mar 17 '20 at 20:14
4

My answer won't solve the problem for everyone, but be sure to check the WKWebView's Attributes Inspector in the Storyboard to see if the changes you want to make to the configuration can be set there.

I just spent an hour trying to figure this out in code, when all I had to do was check 2 boxes in the Attributes Inspector.

iOS_Mouse
  • 754
  • 7
  • 13
1

for Objective C:

WKWebViewConfiguration *config = _webView.configuration;
WKUserContentController* ctrl = [[WKUserContentController alloc] init];
[ctrl addScriptMessageHandler:self name:@"method1"];
[ctrl addScriptMessageHandler:self name:@"method2"];
// others ... 
config.userContentController = ctrl;

And add the implement for WKScriptMessageHandler:

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
NSLog(@"userContentController:didReceiveScriptMessage: message=%@", message);
if ([message.name isEqualToString:@"method1"] && message.body
//        &&[message.body isKindOfClass:[NSString class]]
    ) {
        // do something
    } else if ([message.name isEqualToString:@"method2"] && message.body
        ) {
        // do something
    }
}
Shrdi
  • 359
  • 4
  • 13