Turn--ios JS and native OC call each other

Source: Internet
Author: User
Tags call back

iOS js and native OC invoke each other's introduction

Have been heard WKWebView than UIWebView strong many, but has not been used, today took a little time to read an example, the use of its API to understand, in order to be less detours in the future, but also to make it easier for everyone to learn to get started, here write this article to record how to use and need to pay attention to the place.

Warm hint: I in the study use process, indeed have this experience, WKWebView indeed than UIWebView powerful many, with JS interactive ability display enhancement, in loading speed has improved.

Wkwebview new Features
    • Improved performance, stability, and functionality
    • Allows JavaScript's nitro library to be loaded and used (limited in UIWebView)
    • Support for more HTML5 features
    • Up to 60fps scrolling refresh rate and built-in gestures
    • GPU Hardware acceleration
    • KVO
    • Refactor UIWebView into Class 14 and 3 protocols to view official documents
Preparatory work

First, we introduce modules in the places we use:

import Webkit

Before learning, it is recommended that you first click to WKWebView read the relevant API inside, read it again, there is a general impression, learn quickly.

The methods of initialization are:

public init()public init(frame: CGRect)public init(frame: CGRect, configuration: WKWebViewConfiguration)

HTML is loaded in UIWebView roughly the same way. The loadFileURL method is usually used to load the server's HTML page or JS, and the loadHTMLString method is usually used to load local HTML or JS:

public func loadRequest(request: NSURLRequest) -> WKNavigation?// 9.0以后才支持   @available(iOS 9.0, *)public func loadFileURL(URL: NSURL, allowingReadAccessToURL readAccessURL: NSURL) -> WKNavigation?// 通常用于加载本地HTML或者JSpublic func loadHTMLString(string: String, baseURL: NSURL?) -> WKNavigation?// 9.0以后才支持@available(iOS 9.0, *)public func loadData(data: NSData, MIMEType: String, characterEncodingName: String, baseURL: NSURL) -> WKNavigation

The three major agents used to interact with it:

    • Wknavigationdelegate, related to page navigation loading
    • Wkuidelegate, with JS Interactive UI display related, compare JS alert, confirm, prompt
    • Wkscriptmessagehandler, which is related to JS interaction, is usually the iOS side injection name, JS end through Window.webkit.messageHandlers. {name}.postmessage () to send a message to the iOS side
Create WKWebView

First, we ViewController abide by the agreement in the first place:

class ViewController: UIViewController, WKScriptMessageHandler, WKNavigationDelegate, WKUIDelegate

We can first create a WKWebView configuration item WKWebViewConfiguration , which can set preferences, the configuration to interact with the Web page, inject objects, inject JS, etc.:

// 创建一个webiview的配置项let configuretion = WKWebViewConfiguration()// Webview的偏好设置configuretion.preferences = WKPreferences()configuretion.preferences.minimumFontSize = 10configuretion.preferences.javaScriptEnabled = true// 默认是不能通过JS自动打开窗口的,必须通过用户交互才能打开configuretion.preferences.javaScriptCanOpenWindowsAutomatically = false// 通过js与webview内容交互配置configuretion.userContentController = WKUserContentController()// 添加一个JS到HTML中,这样就可以直接在JS中调用我们添加的JS方法let script = WKUserScript(source: "function showAlert() { alert(‘在载入webview时通过Swift注入的JS方法‘); }",  injectionTime: .AtDocumentStart,// 在载入时就添加JS  forMainFrameOnly: true) // 只添加到mainFrame中configuretion.userContentController.addUserScript(script)// 添加一个名称,就可以在JS通过这个名称发送消息:// window.webkit.messageHandlers.AppModel.postMessage({body: ‘xxx‘})configuretion.userContentController.addScriptMessageHandler(self, name: "AppModel")

Create an object and follow the proxy:

self.webView = WKWebView(frame: self.view.bounds, configuration: configuretion)self.webView.navigationDelegate = selfself.webView.UIDelegate = self

To load our local HTML page:

let url = NSBundle.mainBundle().URLForResource("test", withExtension: "html")self.webView.loadRequest(NSURLRequest(URL: url!))self.view.addSubview(self.webView);

We add the forward, Back button and add a load progress control display on WebView:

self.progressView = UIProgressView(progressViewStyle: .Default)self.progressView.frame.size.width = self.view.frame.size.widthself.progressView.backgroundColor = UIColor.redColor()self.view.addSubview(self.progressView)self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "上一个页面", style: .Done, target: self, action: "previousPage")self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "下一个页面", style: .Done, target: self, action: "nextPage")
Page forward, back

For the forward and backward event handling is very simple, be careful to determine whether you can back, forward to call:

func previousPage() {    if self.webView.canGoBack {      self.webView.goBack()    }}func nextPage() {    if self.webView.canGoForward {      self.webView.goForward()    }}

Of course, in addition to these methods, there are re-loading and so on.

Wkwebview's KVO

For WKWebView , there are three properties that support KVO, so we can listen for changes in their values, respectively:,,, the loading title estimatedProgress corresponding function is expressed as: whether it is loading, the title of the page, page content loading progress (value is 0.0~1.0)

// 监听支持KVO的属性self.webView.addObserver(self, forKeyPath: "loading", options: .New, context: nil)self.webView.addObserver(self, forKeyPath: "title", options: .New, context: nil)self.webView.addObserver(self, forKeyPath: "estimatedProgress", options: .New, context: nil)

Then you can rewrite the listening method to handle it. Here just take the title of the page, update the loaded progress bar, when the loading is complete, manually invoke the execution of a JS method:

// MARK: - KVOoverride func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {  if keyPath == "loading" {    print("loading")  } else if keyPath == "title" {    self.title = self.webView.title  } else if keyPath == "estimatedProgress" {    print(webView.estimatedProgress)    self.progressView.setProgress(Float(webView.estimatedProgress), animated: true)  }  // 已经完成加载时,我们就可以做我们的事了  if !webView.loading {    // 手动调用JS代码    let js = "callJsAlert()";    self.webView.evaluateJavaScript(js) { (_, _) -> Void in      print("call js alert")    }    UIView.animateWithDuration(0.55, animations: { () -> Void in      self.progressView.alpha = 0.0;    })  }}
Wkuidelegate

We look at the several proxy methods of wkuidelegate , although it is not necessary to implement, but if we have a page called the JS Alert, confirm, prompt method, we should implement the following proxy methods, Then call native's pop-up window here, because with Wkwebview , the alert, confirm, prompt method calls in the HTML will no longer pop up, just the native callback proxy method that translates to iOS:

MARK:-wkuidelegate//This method is called when the JS Alert () method is invoked in HTML, the API is recalled. Note that when you use ' Wkwebview ', the Invoke alert () on the JS side does not display a pop-up window in the html//. Therefore, we need to manually eject the alert for the iOS system here. Func WebView (Webview:wkwebview, Runjavascriptalertpanelwithmessage message:string, initiatedbyframe frame: Wkframeinfo, Completionhandler: ()-Void) {Let alert = Uialertcontroller (title: "Tip", Message:message, preferreds Tyle:. Alert) alert.addaction (uialertaction (title: "OK", Style:.) Default, Handler: {(_), Void in//We must call back JS Completionhandler ()})) Self.presentviewcontroller (a Lert, Animated:true, Completion:nil)}func WebView (Webview:wkwebview, Runjavascriptconfirmpanelwithmessage message:  String, Initiatedbyframe Frame:wkframeinfo, Completionhandler: (Bool), Void) {Let alert = Uialertcontroller (title: "Tip", Message:message, Preferredstyle:. Alert) alert.addaction (uialertaction (title: "OK", Style:.) Default, Handler: {(_), Void in//Click Finish, you can do the corresponding processing, and then callback JS end Completionhandler (True)})) Alert.addaction (Uialertaction (title: "Cancel", Style:.) Cancel, Handler: {(_), Void in//Click Cancel, you can do the appropriate processing, and finally callback JS end Completionhandler (False)})) Self.presentviewcontro Ller (Alert, Animated:true, Completion:nil)}func WebView (Webview:wkwebview, runjavascripttextinputpanelwithprompt  Prompt:string, defaulttext:string, Initiatedbyframe Frame:wkframeinfo, Completionhandler: (String?), Void) {Let Alert = Uialertcontroller (Title:prompt, Message:defaulttext, Preferredstyle:. Alert) Alert.addtextfieldwithconfigurationhandler {(Textfield:uitextfield), Void in Textfield.textcolor = UICol Or.redcolor ()} alert.addaction (Uialertaction (title: "OK", Style:.) Default, Handler: {(_), Void in//before processing, the value is passed to the JS end Completionhandler (alert.textfields![  0].text!) })) Self.presentviewcontroller (Alert, Animated:true, Completion:nil)}func webviewdidclose (webview:wkwebview) {print (__function__)}
Wkscriptmessagehandler

Next, we look WKScriptMessageHandler , this is injected with the JS name, the JS side through the window.webkit.messageHandlers.{InjectedName}.postMessage() method to send messages to native. We need to comply with this Protocol, and then implement its proxy method, you can receive the message, and do the appropriate processing. This protocol has only one method:

// MARK: - WKScriptMessageHandlerfunc userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {  print(message.body)  // 如果在开始时就注入有很多的名称,那么我们就需要区分来处理  if message.name == "AppModel" {    print("message name is AppModel")  }}

This method is quite good API, we give JS a name, it will be automatically converted to JS object, then you can send the message to the native side.

Wknavigationdelegate

There is also a very critical agent WKNavigationDelegate , this agent has a lot of proxy methods, you can control the page navigation.

The order of invocation is:
1, this proxy method is used to handle whether to allow jump navigation. For cross-domain only safari browsers are allowed, and other browsers are not allowed, so we need to handle cross-domain links extra.

// 决定导航的动作,通常用于处理跨域的链接能否导航。WebKit对跨域进行了安全检查限制,不允许跨域,因此我们要对不能跨域的链接// 单独处理。但是,对于Safari是允许跨域的,不用这么处理。func webView(webView: WKWebView, decidePolicyForNavigationAction navigationAction: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void) {    print(__FUNCTION__)    let hostname = navigationAction.request.URL?.host?.lowercaseString    print(hostname)    // 处理跨域问题    if navigationAction.navigationType == .LinkActivated && !hostname!.containsString(".baidu.com") {      // 手动跳转      UIApplication.sharedApplication().openURL(navigationAction.request.URL!)      // 不允许导航      decisionHandler(.Cancel)    } else {      self.progressView.alpha = 1.0      decisionHandler(.Allow)    }}

2, start loading page content will callback this proxy method, and UIWebView the function of the didStartLoad equivalent

func webView(webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {    print(__FUNCTION__)}

3, decide whether to allow the navigation response, if not allowed will not jump to the linked page.

func webView(webView: WKWebView, decidePolicyForNavigationResponse navigationResponse: WKNavigationResponse, decisionHandler: (WKNavigationResponsePolicy) -> Void) {    print(__FUNCTION__)    decisionHandler(.Allow)}

4, invoked when content starts arriving for the main frame. This is the original comment for the API. That is, this API is called back when page content loading arrives at mainframe. If we want to inject any JS into the mainframe, we can add it here.

func webView(webView: WKWebView, didCommitNavigation navigation: WKNavigation!) {  print(__FUNCTION__)}

5. Loading the completed callback

func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {  print(__FUNCTION__)}

If the load fails, the following proxy method is recalled:

func webView(webView: WKWebView, didFailNavigation navigation: WKNavigation!, withError error: NSError) {  print(__FUNCTION__)}

There are some APIs that are not normally required. If we need to handle the redirection, we need to implement the following proxy method to receive it.

func webView(webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {  print(__FUNCTION__)}

If our request requires authorization, certificates, etc., we need to process the following proxy methods to provide the appropriate authorization processing, etc.:

func webView(webView: WKWebView, didReceiveAuthenticationChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {    print(__FUNCTION__)    completionHandler(.PerformDefaultHandling, nil)}

When we terminate the page load, we will be able to handle the following proxy methods, if we do not need to process, then do not implement:

func webViewWebContentProcessDidTerminate(webView: WKWebView) {    print(__FUNCTION__)}
Source

Specific code has been published to Github:https://github.com/coderjackyhuang/wkwebviewtestdemo

Summarize

Apple has provided us with all the features it has, and WKWebView UIWebView it offers more features, expressed as an alternative UIWebView , but WKWebView To be used after ios8.0, so if we use Swift to develop the app, the compatible version starts at 8.0 and can be used directly WKWebView .

We can find that Apple provides more convenient way to make the native and JS interaction easier, by letting native inject the name, and then the JS end automatically converted to JS object, you can in the JS end through the way the object to send messages to the native side. In this way, the interaction between JS and native is simplified.

Turn--ios JS and native OC call each other

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.