主要內容
- Safari除錯
- swift/OC與JS互調
- 增加加載進度條
- 支持JS中alert、confirm、prompt
Safari除錯
設定 —> safari --> 高級,開啟JavaScript、網頁檢查器

打開Safari瀏覽器,選擇除錯的網頁,同樣在js里面可以斷點除錯:


swift/OC與JS互調
這里只介紹Swift,OC可以照著swift翻譯即可
- swift呼叫JS,并傳引數
先定義一個JS函式,如:
function add(params) {
let msg = JSON.stringify(params);
showMsg("呼叫了add函式")
}
swift呼叫JS實際上就是呼叫WKWebView的evaluateJavaScript方法
/* @abstract Evaluates the given JavaScript string.
@param javaScriptString The JavaScript string to evaluate.
@param completionHandler A block to invoke when script evaluation completes or fails.
@discussion The completionHandler is passed the result of the script evaluation or an error.
Calling this method is equivalent to calling `evaluateJavaScript:inFrame:inContentWorld:completionHandler:` with:
- A `frame` value of `nil` to represent the main frame
- A `contentWorld` value of `WKContentWorld.pageWorld`
*/
open func evaluateJavaScript(_ javaScriptString: String, completionHandler: ((Any?, Error?) -> Void)? = nil)
引數javaScriptString就是JS的函式名+引數,如:
呼叫add的JS函式,javaScriptString為"add({\n “name” : “jack”\n})"
可以簡單封裝下呼叫方法:
/// 呼叫JS函式
/// - Parameters:
/// - jsFunName: JS函式名
/// - params: 傳給JS函式的引數
/// - Returns:
func callJSFunction(jsFunName: String, params: Any?) -> Void {
var js = String(format: "%@()", jsFunName)
if let valueStr = params as? String {
js = String(format: "%@('%@')", jsFunName, valueStr)
} else if let valueNum = params as? NSNumber {
js = String(format: "%@(%@)", jsFunName, valueNum)
} else if let valueBool = params as? Bool {
if valueBool {
js = jsFunName + "(true)"
} else {
js = jsFunName + "(false)"
}
} else if let valueDiction = params {
if let temValue = converObjToJson(obj: valueDiction) {
js = String(format: "%@(%@)", jsFunName, temValue)
} else {
js = String(format: "%@()", jsFunName)
}
} else {
js = String(format: "%@()", jsFunName)
}
self.webView?.evaluateJavaScript(js) { (item, error) in
}
}
- JS呼叫swift,并傳引數
在JS端呼叫window.webkit.messageHandlers.自定義一個名稱.postMessage(params),如:
var params = {“name”: “JSName”};
window.webkit.messageHandlers.minus.postMessage(params);
這個自定義的名稱需要提前設定到WKWebView上,設定方法:
/** @abstract Adds a script message handler to the main world used by page content itself.
@param scriptMessageHandler The script message handler to add.
@param name The name of the message handler.
@discussion Calling this method is equivalent to calling addScriptMessageHandler:contentWorld:name:
with [WKContentWorld pageWorld] as the contentWorld argument.
*/
open func add(_ scriptMessageHandler: WKScriptMessageHandler, name: String)
如:
let userContent = WKUserContentController()
// 配置JS可以呼叫的名稱
let jsCallFun: [String] = ["minus"]
for value in jsCallFun {
userContent.add(self, name: value)
}
let config = WKWebViewConfiguration()
config.preferences = preferences
config.userContentController = userContent
config.suppressesIncrementalRendering = true
配置完后,當JS呼叫window.webkit.messageHandlers.minus.postMessage(params);時會進入到WSWebView的WKScriptMessageHandler代理方法userContentController
如:
// MARK: - WKScriptMessageHandler
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
let name = message.name // 注冊過的JS可呼叫的名稱
if name.elementsEqual("minus") {
if let params = message.body as? NSDictionary { // JS傳遞的引數
if let name = params.value(forKey: "name") as? String {
minus(name: name)
}
}
}
}
func minus(name: String) -> Void {
showAlert(title: "Swift minus方法", message: "name:\(name)", modelArray: nil, cancelTitle: "確定", cancelHandle: {
}, vc: self, sourceView: webView)
}
總結:
我們常用的需求更多的場景應該是JS呼叫swift的某個方法后,再回呼JS的某個方法,達到異步回呼的效果,這時我們可以這樣設計:
把JS的回呼函式名當作引數傳給swift的方法,swift處理完邏輯后直接呼叫這個引數:
var params = {"name": "JSName","callBackFun": "minusCallback"};
window.webkit.messageHandlers.minus.postMessage(params);
而swift端:
// MARK: - WKScriptMessageHandler
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
let name = message.name // 注冊過的JS可呼叫的名稱
if name.elementsEqual("minus") {
if let params = message.body as? NSDictionary { // JS傳遞的引數
if let name = params.value(forKey: "name") as? String {
let callback = params.value(forKey: "callBackFun") as? String
minus(name: name, callBack: callback)
}
}
}
}
func minus(name: String, callBack: String?) -> Void {
showAlert(title: "Swift minus方法", message: "name:\(name)", modelArray: nil, cancelTitle: "確定", cancelHandle: { [weak self] in
if let callBackFun = callBack {
guard let self = self else { return }
self.callJSFunction(jsFunName: callBackFun, params: nil)
}
}, vc: self, sourceView: webView)
}
swift呼叫JS同理!
增加加載進度條
進度條使用第三方庫NJKWebViewProgress
pod 'NJKWebViewProgress'
在WKWebView上面預留2個高度顯示進度條,使用ReactiveCocoa監聽WKWebView的進度:
self.webView?.reactive.signal(forKeyPath: "estimatedProgress").observeValues({ [weak self](result) in
guard let self = self else {return}
if let progress = result as? NSNumber {
self.progressView?.setProgress(progress.floatValue, animated: true)
if progress.floatValue >= 1.0 {
self.progressWrapViewHeightConst.constant = 0
}
}
})
其他地方涉及到進度的地方再優化下:
// MARK: - WKNavigationDelegate
// MARK: 頁面開始加載時呼叫
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
self.progressWrapViewHeightConst.constant = self.progressHeight
}
// MARK: 內容開始回傳時呼叫
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
self.progressWrapViewHeightConst.constant = 0
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
self.progressWrapViewHeightConst.constant = 0
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.progressWrapViewHeightConst.constant = 0
}
支持JS中alert、confirm、prompt
在JS端呼叫alert、confirm、prompt這幾個方法,實際上會回呼到WKWebView的WKUIDelegate代理中,對應的方法為:
@available(iOS 8.0, *)
optional func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void)
@available(iOS 8.0, *)
optional func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void)
@available(iOS 8.0, *)
optional func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void)
需要實作具體方法才能有效果:
// MARK: - WKUIDelegate
func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
showAlert(title: "溫馨提示", message: message, modelArray: nil, cancelTitle: "確定", cancelHandle: {
completionHandler()
}, vc: self, sourceView: webView)
}
func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
var modelArray: [AlertModel] = [AlertModel]()
let model = AlertModel(title: "確定") {
completionHandler(true)
}
modelArray.append(model)
showAlert(title: "溫馨提示", message: message, modelArray: modelArray, cancelTitle: "取消", cancelHandle: {
completionHandler(false)
}, vc: self, sourceView: webView)
}
func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
let alertController = UIAlertController(title: prompt, message: defaultText, preferredStyle: UIAlertController.Style.alert)
alertController.addTextField { (txtField) in
txtField.text = defaultText
}
alertController.addAction(UIAlertAction(title: "確定", style: UIAlertAction.Style.default, handler: { (alertAction) in
let txt = alertController.textFields?[0].text
completionHandler(txt)
}))
self.present(alertController, animated: true) {
}
}
專案原始碼: https://codechina.csdn.net/ios1/projectcommon
如果覺得可以就點個👍吧,歡迎粉絲收藏,土豪打賞,您的關注就是我們創作的動力!
讀者有什么想看的相關技術篇章,歡迎評論留言!
QQ交流群:908058499
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/272017.html
標籤:其他
下一篇:應用編程-行程信號處理
