IOS UIWebView与Javascript之间的交互

因为项目需要做一个活动,而这个活动的信息是源于HTML5写的,而这个操作网页的过程上,

是需要到与原生APP这边交互的,因为这就增加了一个需求,那就是与JS交互。

目前很流行的库有WebviewJavaScriptBridge和OVGap,这两个库都是让webview与JS建立起一条桥梁,

这样就可以相互通信了。

花了两天的时间,反反复复地研究了WebviewJavaScriptBridge和OVGap这两个库,也在网上搜索了很多的

相关博客看,可是都没有满足我的需求。网上的教程几乎都是webview给调用JS,使用系统提供的方法,这是

想学Easy就可以做到的,但是如果想让JS调用我们的原生的方法,那就不容易了,就需要一条桥梁,在JS响应的

时候能回调OC的方法。这两个库都是可以满足我们的,但是在JS端需要添加对应的JS,对于前者,还需要把响应的

方法放到桥梁内,如:

[objc]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. function connectWebViewJavascriptBridge(callback) {  
  2.     if (window.WebViewJavascriptBridge) {  
  3.         callback(WebViewJavascriptBridge)  
  4.     } else {  
  5.         document.addEventListener('WebViewJavascriptBridgeReady', function() {  
  6.             callback(WebViewJavascriptBridge)  
  7.         }, false)  
  8.     }  
  9. }  
  10.   
  11. connectWebViewJavascriptBridge(function(bridge) {  
  12.     var uniqueId = 1  
  13.     function log(message, data) {  
  14.         var log = document.getElementById('log')  
  15.         var el = document.createElement('div')  
  16.         el.className = 'logLine'  
  17.         el.innerHTML = uniqueId++ + '. ' + message + ':<br/>' + JSON.stringify(data)  
  18.         if (log.children.length) { log.insertBefore(el, log.children[0]) }  
  19.         else { log.appendChild(el) }  
  20.     }  
  21.     bridge.init(function(message, responseCallback) {  
  22.         log('JS got a message', message)  
  23.         var data = { 'Javascript Responds':'Wee!' }  
  24.         log('JS responding with', data)  
  25.         responseCallback(data)  
  26.     })  
  27.   
  28.     bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {  
  29.         log('ObjC called testJavascriptHandler with', data)  
  30.         var responseData = { 'Javascript Says':'Right back atcha!' }  
  31.         log('JS responding with', responseData)  
  32.         responseCallback(responseData)  
  33.     })  
  34.   
  35.     var button = document.getElementById('buttons').appendChild(document.createElement('button'))  
  36.     button.innerHTML = 'Send message to ObjC'  
  37.     button.onclick = function(e) {  
  38.         e.preventDefault()  
  39.         var data = 'Hello from JS button'  
  40.         log('JS sending message', data)  
  41.         bridge.send(data, function(responseData) {  
  42.             log('JS got response', responseData)  
  43.         })  
  44.     }  
  45.   
  46.     document.body.appendChild(document.createElement('br'))  
  47.   
  48.     var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button'))  
  49.     callbackButton.innerHTML = 'Fire testObjcCallback'  
  50.     callbackButton.onclick = function(e) {  
  51.         e.preventDefault()  
  52.         log('JS calling handler "testObjcCallback"')  
  53.         bridge.callHandler('testObjcCallback', {'foo''bar'}, function(response) {  
  54.             log('JS got response', response)  
  55.         })  
  56.     }  
  57. })  
  58. </script>  

connectWebViewJavascriptBridge

这个方法是必须的,而响应要放在这个方法中,这样对安卓端可能会中影响,于是放弃了这个库的使用。


将下来是使用OVGap这个库。

使用这个库前,需要给HTML5中引入对方的脚本,叫ovgap.js,可到Github下载:

[javascript]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. ;(function() {  
  2.   
  3. var require, define;  
  4.   
  5. (function () {  
  6.     var modules = {},  
  7.     // Stack of moduleIds currently being built.  
  8.         requireStack = [],  
  9.     // Map of module ID -> index into requireStack of modules currently being built.  
  10.         inProgressModules = {},  
  11.         SEPERATOR = ".";  
  12.   
  13.     function build(module) {  
  14.         var factory = module.factory,  
  15.             localRequire = function (id) {  
  16.                 var resultantId = id;  
  17.                 //Its a relative path, so lop off the last portion and add the id (minus "./")  
  18.                 if (id.charAt(0) === ".") {  
  19.                     resultantId = module.id.slice(0, module.id.lastIndexOf(SEPERATOR)) + SEPERATOR + id.slice(2);  
  20.                 }  
  21.                 return require(resultantId);  
  22.             };  
  23.         module.exports = {};  
  24.         delete module.factory;  
  25.         factory(localRequire, module.exports, module);  
  26.         return module.exports;  
  27.     }  
  28.   
  29.     require = function (id) {  
  30.         if (!modules[id]) {  
  31.             throw "module " + id + " not found";  
  32.         } else if (id in inProgressModules) {  
  33.             var cycle = requireStack.slice(inProgressModules[id]).join('->') + '->' + id;  
  34.             throw "Cycle in require graph: " + cycle;  
  35.         }  
  36.         if (modules[id].factory) {  
  37.             try {  
  38.                 inProgressModules[id] = requireStack.length;  
  39.                 requireStack.push(id);  
  40.                 return build(modules[id]);  
  41.             } finally {  
  42.                 delete inProgressModules[id];  
  43.                 requireStack.pop();  
  44.             }  
  45.         }  
  46.         return modules[id].exports;  
  47.     };  
  48.   
  49.     define = function (id, factory) {  
  50.         if (modules[id]) {  
  51.             throw "module " + id + " already defined";  
  52.         }  
  53.   
  54.         modules[id] = {  
  55.             id: id,  
  56.             factory: factory  
  57.         };  
  58.     };  
  59.   
  60.     define.remove = function (id) {  
  61.         delete modules[id];  
  62.     };  
  63.   
  64.     define.moduleMap = modules;  
  65. })();  
  66.   
  67.   
  68.   
  69. define("ov_gap"function(require, exports, module) {  
  70.   
  71. var ovGap = {  
  72.     callbackId: Math.floor(Math.random() * 2000000000),  
  73.     callbacks: {},  
  74.     commandQueue: [],  
  75.     groupId: Math.floor(Math.random() * 300),  
  76.     groups: {},  
  77.     listeners: {},  
  78.     invoke: function(cmd, params, onSuccess, onFail) {  
  79.         if(!cmd) cmd = "defaultCommand";  
  80.         if(!params) params = {};  
  81.         this.callbackId ++;  
  82.         this.callbacks[this.callbackId] = {  
  83.             success: onSuccess,  
  84.             fail: onFail  
  85.         };  
  86.         var rurl = "ovgap://" + cmd + "/" + JSON.stringify(params) + "/" + this.callbackId;  
  87.         document.location = rurl;  
  88.     },   
  89.     dispatchCommand: function(cmd, params, onSuccess, onFail) {  
  90.         if(!cmd) cmd = "defaultCommand";  
  91.         if(!params) params = {};  
  92.         this.callbackId ++;  
  93.         this.callbacks[this.callbackId] = {  
  94.             success: onSuccess,  
  95.             fail: onFail  
  96.         };  
  97.         var command = cmd + "/" + JSON.stringify(params) + "/" + this.callbackId;  
  98.         this.commandQueue.push(command);  
  99.     },  
  100.     fetchNativeCommands: function() {  
  101.         var json = JSON.stringify(this.commandQueue);  
  102.         this.commandQueue = [];  
  103.         return json;  
  104.     },  
  105.     activate: function() {  
  106.         document.location = "ovgap://ready";  
  107.     },  
  108.     // return group ID  
  109.     createGroup: function() {  
  110.         this.groupId ++;  
  111.         this.groups[this.groupId] = [];  
  112.         return this.groupId;  
  113.     },  
  114.     dispatchCommandInGroup: function(cmd, params, onSuccess, onFail, groupId) {  
  115.         if (!this.groups[groupId]) return false;  
  116.   
  117.         if(!cmd) cmd = "defaultCommand";  
  118.         if(!params) params = {};  
  119.         this.callbackId ++;  
  120.         this.callbacks[this.callbackId] = {  
  121.             success: onSuccess,  
  122.             fail: onFail  
  123.         };  
  124.         var command = cmd + "/" + JSON.stringify(params) + "/" + this.callbackId;  
  125.         this.groups[groupId].push(command);  
  126.         return true;  
  127.     },  
  128.     activateGroup: function(groupId) {  
  129.         if (!this.groups[groupId]) return false;  
  130.         document.location = "ovgap://group/" + groupId;  
  131.     },  
  132.     fetchNativeGroupCommands: function(groupId) {  
  133.         if (!this.groups[groupId]) return [];  
  134.         var json = JSON.stringify(this.groups[groupId]);  
  135.         this.groups[groupId] = [];  
  136.         return json;  
  137.     },  
  138.     callbackSuccess: function(callbackId, params) {  
  139.         try {  
  140.             ovGap.callbackFromNative(callbackId, params, true);  
  141.         } catch (e) {  
  142.             console.log("Error in error callback: " + callbackId + " = " + e);  
  143.         }  
  144.     },  
  145.     callbackError: function(callbackId, params) {  
  146.         try {  
  147.             ovGap.callbackFromNative(callbackId, params, false);  
  148.         } catch (e) {  
  149.             console.log("Error in error callback: " + callbackId + " = " + e);  
  150.         }   
  151.     },   
  152.     callbackFromNative: function(callbackId, params, isSuccess) {  
  153.         var callback = this.callbacks[callbackId];  
  154.         if (callback) {  
  155.             if (isSuccess) {  
  156.                 callback.success && callback.success(callbackId, params);  
  157.             } else {  
  158.                 callback.fail && callback.fail(callbackId, params);  
  159.             }  
  160.             delete ovGap.callbacks[callbackId];  
  161.         };  
  162.     },  
  163.     addGapListener: function(listenId, onSuccess, onFail) {  
  164.         if (!listenId || !onSuccess || !onFail) return;  
  165.         this.listeners[listenId] = {  
  166.             success : onSuccess,   
  167.             fail : onFail  
  168.         };  
  169.     },  
  170.     removeListener: function(listenId) {  
  171.         if (!this.listeners[listenId]) return;  
  172.         this.listeners[listenId] = null;  
  173.     },  
  174.     triggerListenerSuccess: function(listenId, params) {  
  175.         if (!this.listeners[listenId]) return;  
  176.         var listener = this.listeners[listenId];  
  177.         listener.success && listener.success(listenId, params);  
  178.     },  
  179.     triggerListenerFail: function(listenId, params) {  
  180.         if (!this.listeners[listenId]) return;  
  181.         var listener = this.listeners[listenId];  
  182.         listener.fail && listener.fail(listenId, params);  
  183.     }  
  184. };  
  185.   
  186. module.exports = ovGap;  
  187.   
  188. });  
  189.   
  190. window.ov_gap = require("ov_gap");  
  191.   
  192. }) ();  

给按钮添加一个点击事件,回调如下:

[javascript]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. function onButtonClick() {  
  2.  // 下面是我需要处理的事,处理完之后  
  3.  alert('这里我是要处理一些事的,如果有需要的话。');  
  4.    
  5.  // 这里是回调我们前端与后端商量好的方法  
  6.  // activityList是oc中的方法  
  7.   window.ov_gap.invoke("activityList"null, success, fail);  
  8. }  

这样就从JS回调到了IOS端的OC方法,然后处理我们想做的事。




可是这两个库都需要添加这些东西,做HTML5的人可不愿意,因为这样的话,对于IOS的处理是一种,对于安卓和WP呢?又得写一份吗?

于是我又去寻找别的库,有一个叫apache cordova的库,是支持ios,android,wp的,可是太大了,又是英文的,安装也很困难,学习成本太高,

于是看了看就放弃了,这么大的库,给我们带来的可不一定是好处多于动坏处啊。


转了一圈又回到了原生的,有一个库叫JavaScriptCore,这个是IOS7以后才开放的API,这可以极大的简化了我们的需求,非常的简单,

我们只需要注入一个方法,就可以在JS中调用此方法来跟原生的OC交互。

首先得加入库

[javascript]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. #import <JavaScriptCore/JavaScriptCore.h>  

JSContext这个可是关键。

[objc]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. // webView对象  
  2. @property (nonatomicstrongreadonly) UIWebView    *webView;  
  3. @property (nonatomicstrongreadonly) JSContext    *jsContext;  

下面是在webview加载完成后, 关联JS与OC:

[objc]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. - (void)webViewDidFinishLoad:(UIWebView *)webView {  
  2.   [_activityView stopAnimating];  
  3.   
  4.   [self dismiss];  
  5.   if (_jsContext == nil) {  
  6.     // 1.  
  7.     _jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];  
  8.       
  9.     // 2. 关联打印异常  
  10.     _jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {  
  11.       context.exception = exceptionValue;  
  12.       DDLogVerbose(@"异常信息:%@", exceptionValue);  
  13.     };  
  14.   
  15.     _jsContext[@"activityList"] = ^(NSDictionary *param) {  
  16.       DDLogVerbose(@"%@", param);  
  17.     };  
  18.       
  19.     // Mozilla/5.0 (iPhone; CPU iPhone OS 10_10 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B411  
  20.     id userAgent = [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];  
  21.     DDLogVerbose(@"%@", userAgent);  
  22.   }  
  23. }  

上面

activityList是商定好的方法名称,在JS中写法:

[javascript]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. <input type="button" value="测试log" οnclick="activityList({'tytyty':'hehe'})" />  
在点击的时候,直接回调是可以的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值