- WKWebView 功能測試 (上) - 建置、設定、呈現
- WKWebView 功能測試 (下) - 與 Javascript 交流
使用 Javascript 連結網頁與 APP
最後要測試的是最重要的功能,也就是使用 Javascript 連結網頁與 APP。
希望達到的功能有二種:
-
由 APP 向網頁注入 Javascript,並取得傳回資料。
-
網頁的 Javascript 執行 APP 的功能並傳資料給 APP。
有了這雙方溝通的功能,就可以做出很多應用了。
由 APP 向網頁注入 Javascript
在這裡我們要設計二個測試:
1. 把 APP 輸入欄位的內容寫到網頁中的 Label 中。
2. 可以執行網頁原本就有的 GetSel 函式,該函式就是把選取的文字寫在網頁上,並且傳回去。APP 取得後再填到輸入欄位上。
先準備網頁 index.htm
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Index</title>
<link rel='stylesheet' type='text/css' href='mycss.css'>
<script type="text/javascript" src="myjs.js"></script>
</head>
<body>
<script>
function GetSel()
{
str = window.getSelection().toString();
ShowLabel(str);
return str;
}
</script>
<h1>Hello World!</h1>
<div>Hi This <span style="color:red">is a test</span> html.</div>
<div>My Label : <span id="MyLabel"></span></div>
<a href="#" onclick="ShowLabel('This is a Label.')">func ShowLabel()</a><br>
<a href="#" onclick="GetSel();">func GetSel()</a><br>
<a href="index2.htm">Goto index2</a>
</body>
</html>
外部檔案的 myjs.js
function ShowLabel(str)
{
document.getElementById("MyLabel").innerText = str;
}
向網頁注入 Javascript 是用 evaluateJavaScript 來實現的。 [官方文件]
在 WebView 上放置二個按鈕,一個輸入欄位。
第一個按鈕按下時,要將輸入欄位的文字填入網頁指定的位置。
@IBAction func btSendWebPageTextClick(_ sender: Any) {
let strLabel = edEdit.stringValue
let strJS = "document.getElementById('MyLabel').innerText = '\(strLabel)'"
webView.evaluateJavaScript(strJS)
}
這個功能沒有傳回值,執行如果如下:
先在紅圈 1 的輸入欄輸入文字,接著按下紅圈 2 的按鈕,就會看到輸入的文字出現在網頁紅框 3 的位置。
另一個按鈕程式如下:
@IBAction func btGetWebPageTextClick(_ sender: Any) {
webView.evaluateJavaScript("GetSel()",
completionHandler: {
(result, err) in
self.edEdit.stringValue = result as! String
})
}
這個程式是直接呼叫網頁中原有的 Javascript 函式 GetSel(),它的作用是把使用者選取的文字貼在網頁上 Lable 的位置,並且傳回文字。
程式中的 completionHandler 就是負責處理接收 Javascript 傳回的訊息,result 是傳回內容,err 是錯誤訊息。
上面沒有處理錯誤訊息,只是把傳回的 result 轉成字串,然後貼在 APP 的輸入欄位中。
執行結果如下。
先選取一段文字 (紅圈1),按下紅圈 2 的按鈕,執行 GetSel() 函式會把選取的文字貼在紅圈 3 的位置,這是網頁本身 Javascript 的功能。最後傳回該文字,由 APP 接受之後,呈現在紅圈 4 的輸入欄位中。
網頁的 Javascript 執行 APP 的功能
這個功能是指在 APP 中設定一個函式,而網頁中的 Javascript 可以呼叫這個函式,也可以利用這個函式把資料傳給 APP,等於是由網頁向 APP 傳送訊息。
這裡要再做一個測試,也就是在網頁中加個連結,如果點選網頁的連結,就會把選取的文字送給 APP。
而 APP 也會做一個接收的函式,若收到網頁傳來的文字,就把它呈現在輸入欄位中。
底下逐一說明實作:
我在某個網頁看到如下的作法:
// 先產生 WebViewConfiguration
let webConfiguration = WKWebViewConfiguration()
// 再產生它的 userContentController
webConfiguration.userContentController = WKUserContentController()
// 再指定 AppFunc 這個函式是可供 Javascript 呼叫的進入點
webConfiguration.userContentController.add(self, name: "AppFunc")
// 然後用 webConfig 來建立 webView
var webView = WKWebView(frame: .zero, configuration: webConfiguration)
不過我先前的程式是已經先產生 WebView 了,所以底下的方法是事後才把 WebViewConfiguration 加入 WebView 之中,測試之後也是可行的,程式如下:
// 取出 WebView 的 WebViewConfiguration
let webConfiguration = webView.configuration
// 將它加上 AppFunc 這個供 Javascript 呼叫的進人點
webConfiguration.userContentController.add(self, name: "AppFunc")
WebViewConfiguration 的 [官方文件]。
因為要接收網頁 Javascript 傳回的訊息,因此 ViewController 要繼承 WKScriptMessageHandler。
class ViewController: NSViewController, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler {
同時要加上這個特定函式來接受 Javascript 傳來的訊息,它要做的事只有一件,就是把傳回來的文字呈現在輸入欄位上。
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
edEdit.stringValue = message.body as! String
}
message.body 是傳回的訊息,而 message.name 是進入點的名稱 "AppFunc"。如果加了好幾組進入點,就可以由 message.name 來判斷是哪一個函式傳回來的。
最後是 HTML 網頁中的呼叫方法,只要加上一個連結。
<a href="#" onclick="CallApp();">App Func()</a>
而 Javascript 如下,最重要的是要呼叫
window.webkit.messageHandlers.AppFunc.postMessage()
那個 AppFunc 就是前面設計的進入點名稱。這個程式就是把使用者選取的文字傳給 APP。
function CallApp()
{
str = window.getSelection().toString();
window.webkit.messageHandlers.AppFunc.postMessage(str);
}
執行如果如下。
使用者先選取文字 (紅圈 1),然後按下網頁中紅圈 2 的 App Func() 連結,就會執行 CallApp(),也就是把選取的文字傳給 APP,APP 再把它呈現在紅圈 3 的位置。
至此,我們就可以讓 APP 與網頁互相溝通無礙了。
最後呈現完整網頁與程式。
index.htm
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Index</title>
<link rel='stylesheet' type='text/css' href='mycss.css'>
<script type="text/javascript" src="myjs.js"></script>
</head>
<body>
<script>
function GetSel()
{
str = window.getSelection().toString();
ShowLabel(str);
return str;
}
function CallApp()
{
str = window.getSelection().toString();
window.webkit.messageHandlers.AppFunc.postMessage(str);
}
</script>
<h1>Hello World!</h1>
<div>Hi This <span style="color:red">is a test</span> html.</div>
<div>My Label : <span id="MyLabel"></span></div>
<a href="#" onclick="ShowLabel('This is a Label.')">func ShowLabel()</a><br>
<a href="#" onclick="GetSel();">func GetSel()</a><br>
<a href="index2.htm">Goto index2</a><br><br>
<a href="#" onclick="CallApp();">App Func()</a>
</body>
</html>
程式碼
import Cocoa
import WebKit
class ViewController: NSViewController, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler {
// MARK: 自訂屬性與元件
let webView = WKWebView()
@IBOutlet weak var edEdit: NSTextField!
// MARK: 預設成員函式
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupWebview() // 設定 webView
self.view.addSubview(webView)
webviewConstraint() // 約束 webView
loadWebview() // 載入網頁
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
// MARK: 自訂成員函式
// 設定 webView
fileprivate func setupWebview() {
self.webView.uiDelegate = self
self.webView.navigationDelegate = self
let webConfiguration = webView.configuration
webConfiguration.userContentController.add(self, name: "AppFunc")
}
// 接受網頁傳回的資訊
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
edEdit.stringValue = message.body as! String
}
// 約束 webView
fileprivate func webviewConstraint() {
webView.translatesAutoresizingMaskIntoConstraints = false
let ctLeft = NSLayoutConstraint(item: webView, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leading, multiplier: 1, constant: 20)
let ctRight = NSLayoutConstraint(item: webView, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailing, multiplier: 1, constant: -20)
let ctTop = NSLayoutConstraint(item: webView, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 50)
let ctBottom = NSLayoutConstraint(item: webView, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1, constant: -20)
NSLayoutConstraint.activate([ctLeft,ctRight,ctTop,ctBottom])
}
// 載入網頁
fileprivate func loadWebview() {
webView.loadFileURL(URL(string: "file:///Users/heaven/desktop/html/index.htm")!, allowingReadAccessTo: URL(string: "file:///Users/heaven/desktop/html")!)
}
// MARK: 元件綁定的事件
@IBAction func btSendWebPageTextClick(_ sender: Any) {
let strLabel = edEdit.stringValue
let strJS =
"document.getElementById('MyLabel').innerText = '\(strLabel)'"
webView.evaluateJavaScript(strJS)
}
@IBAction func btGetWebPageTextClick(_ sender: Any) {
webView.evaluateJavaScript("GetSel()",
completionHandler: {
(result, err) in
self.edEdit.stringValue = result as! String
})
}
}
- WKWebView 功能測試 (上) - 建置、設定、呈現
- WKWebView 功能測試 (下) - 與 Javascript 交流
- 瀏覽次數:15535
發表新回應