Capturing window.print() from a WKWebView
Asked Answered
H

2

6

I'm trying to capture the window.print() javascript call from a WKWebView with my app to actually show a print dialog and allow them to print from a button presented on the page (instead of a button inside my app). Does anyone know the best way to accomplish this?

Currently, for me, clicking a link that calls window.print() just does nothing but fire the decidePolicyForNavigationAction delegate method and I couldn't find anything relevant in there.

Hypo answered 16/11, 2014 at 9:31 Comment(2)
Can I ask which view you are printing?Brigantine
I'm printing the WKWebView's viewPrintFormatterHypo
H
15

Figured it out just after posting (obviously)... Hopefully this helps someone else.

When creating your web view...

let configuration = WKWebViewConfiguration()
let script = WKUserScript(source: "window.print = function() { window.webkit.messageHandlers.print.postMessage('print') }", injectionTime: WKUserScriptInjectionTime.AtDocumentEnd, forMainFrameOnly: true)
configuration.userContentController.addUserScript(script)
configuration.userContentController.addScriptMessageHandler(self, name: "print")
self.webView = WKWebView(frame: CGRectZero, configuration: configuration)

Then just adopt the WKScriptMessageHandler protocol in your view controller and add the required method...

func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
    if message.name == "print" {
        printCurrentPage()
    } else {
        println("Some other message sent from web page...")
    }
}

func printCurrentPage() {
    let printController = UIPrintInteractionController.sharedPrintController()
    let printFormatter = self.webView.viewPrintFormatter()
    printController?.printFormatter = printFormatter

    let completionHandler: UIPrintInteractionCompletionHandler = { (printController, completed, error) in
        if !completed {
            if let e = error? {
                println("[PRINT] Failed: \(e.domain) (\(e.code))")
            } else {
                println("[PRINT] Canceled")
            }
        }
    }

    if let controller = printController? {
        if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
            controller.presentFromBarButtonItem(someBarButtonItem, animated: true, completionHandler: completionHandler)
        } else {
            controller.presentAnimated(true, completionHandler: completionHandler)
        }
    }
}
Hypo answered 30/11, 2014 at 23:46 Comment(2)
What is the self.browserController.addressBarActivityButton for iPad?Finality
On iPad you have to present the print controller from a button/view. That's just a bar button item I'm presenting from. Left over from copying code from my project.Hypo
D
0

there is an undocumented delegate for hooking window.print()s

class MyApp: WKUIDelegate {
  func makeWebview() {
      ...
      self.webview.UIDelegate = self
  }
  func _webView(webView: WKWebView!, printFrame: WKFrameInfo) {
      println("JS: window.print()")
      printCurrentPage()
  }     
}
Deflocculate answered 5/2, 2015 at 8:25 Comment(2)
Seems strange that they exposed the delegate method for creating a new window but kept this one private. Hopefully they change this in the future. If you don't need your app to be approved by apple, this is probably a good way to go.Hypo
WebView "private" methods aren't so private, plenty of people use them in App Store submissions and they even support the underscored function signatures after promoting them to public methods. Case in point, Apple has added a new private method to WKWebView in iOS9/OSX10.11: _printOperationWithPrintInfo: github.com/WebKit/webkit/commit/…Deflocculate

© 2022 - 2024 — McMap. All rights reserved.