本文所说的APP指IOS APP(Swift),不包括Android APP。
APP如何渲染网页?
苹果提供了UIWebView组件,像浏览器一样可以加载任何网页。iOS8以后,苹果推出了新框架Webkit,提供了替换UIWebView的组件WKWebView。速度更快,占用内存少。
WKWebView
执行JavaScript
//home.html //... <h1>Welcome!</h1> //... <script> function redHeader() { document.querySelector('h1').style.color = "red"; } </script>
下面的代码会执行JavaScript的redHeader方法将h1变成红色。
//swift webView.evaluateJavaScript("redHeader()") { (value, error) in print(value ?? "") }
消息
APP可以通过WKWebView执行JavaScript代码,JavaScript要执行APP的代码又该如何实现?
WKWebView可以添加脚本消息处理程序,使JavaScript可以直接调用并传递消息。
//swift let contentController = WKUserContentController() contentController.add(self,name: "callbackHandler") let config = WKWebViewConfiguration() config.userContentController = contentController
JavaScript发送消息:
//javascript window.webkit.messageHandlers.callbackHandler.postMessage({ title: "Hello!", message: 'Welcome!' });
APP在接收到消息后弹出:
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { if let body = message.body as? NSDictionary { let alert = UIAlertController(title: body.object(forKey: "title")! as? String, message: body.object(forKey: "message")! as? String, preferredStyle: UIAlertControllerStyle.alert) alert.addAction(UIAlertAction(title: "Continue", style: UIAlertActionStyle.default, handler: nil)) alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil)) self.present(alert, animated: true, completion: nil) } }
拦截弹出框
通常情况下JavaScript弹出来的弹出框都会带上一个url,如果你的网页经常需要运行在微信端,下面的效果一定会经常见到:
你不得不去模拟一个半透明的浮层和一个弹出框,如果你足够的严谨你甚至会模拟一个细边框(业界统称1像素)。
通常情况下我只想alert("test!")
,还不想让弹出框显示url,该怎么办?APP可以拦截到弹出框并自定义样式和内容。
//swift func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) { let alertController = UIAlertController(title: "提醒", message: message, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in completionHandler() })) self.present(alertController, animated: true, completion: nil) }
同理,confirm与prompt也是可以被拦截并自定义样式的。
jsbridge
WKWebView不仅能拦截弹出框,同样能拦截到所有的请求,如果在请求后边加上参数,岂不是同样可以达到通信的目的。
约定一个协议,例如,当链接触发的时候弹出message:
//html <a href="jsbridge://alert?title=Notice&message=Lauching this missile will destroy the entire universe. Is this what you intended to do?">jsbridge</a>
APP拦截请求弹出:
//swift func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { let url = navigationAction.request.url let scheme = url?.scheme let method = url?.host let query = url?.query if url != nil && scheme == "jsbridge" { switch method! { case "alert": //url.valueOf("test1") let alert = UIAlertController(title: url?.valueOf(queryParamaterName: "title"), message: url?.valueOf(queryParamaterName: "message"), preferredStyle: UIAlertControllerStyle.alert) //add the actions (buttons) alert.addAction(UIAlertAction(title: "Continue", style: UIAlertActionStyle.default, handler: nil)) alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.cancel, handler: nil)) // show the alert self.present(alert, animated: true, completion: nil) default: print("default") } decisionHandler(.cancel) } else { decisionHandler(.allow) } }
利用jsbridge不仅仅能实现弹框,可以实现更多交互,例如分享…
UIWebView
执行JaVascript
下面的代码会获取到标题并输出,接着修改页面标题为红色:
//swift let title:String = webView.stringByEvaluatingJavaScript(from: "document.title")! print("title:\(title)" ) //redHeader webView.stringByEvaluatingJavaScript(from: "redHeader()")
UIWebView不能发消息但是可以拦截请求,与WKWebView通过jsbridge通信方法一致。