美文网首页
iOS的UIWebView与Javascript之间的交互

iOS的UIWebView与Javascript之间的交互

作者: 祥子_HelloWorld | 来源:发表于2019-08-04 01:59 被阅读0次

目前很流行的库有WebviewJavaScriptBridge和OVGap,这两个库都是让webview与JS建立起一条桥梁,这样就可以相互通信了。

网上的教程几乎都是webview给调用JS,使用系统提供的方法,这是想学Easy就可以做到的,但是如果想让JS调用我们的原生的方法,那就不容易了,就需要一条桥梁,在JS响应的时候能回调OC的方法。这两个库都是可以满足我们的,但是在JS端需要添加对应的JS,对于前者,还需要把响应的方法放到桥梁内,如:

function connectWebViewJavascriptBridge(callback) {  
    if (window.WebViewJavascriptBridge) {  
        callback(WebViewJavascriptBridge)  
    } else {  
        document.addEventListener('WebViewJavascriptBridgeReady', function() {  
            callback(WebViewJavascriptBridge)  
        }, false)  
    }  
}  
  
connectWebViewJavascriptBridge(function(bridge) {  
    var uniqueId = 1  
    function log(message, data) {  
        var log = document.getElementById('log')  
        var el = document.createElement('div')  
        el.className = 'logLine'  
        el.innerHTML = uniqueId++ + '. ' + message + ':<br/>' + JSON.stringify(data)  
        if (log.children.length) { log.insertBefore(el, log.children[0]) }  
        else { log.appendChild(el) }  
    }  
    bridge.init(function(message, responseCallback) {  
        log('JS got a message', message)  
        var data = { 'Javascript Responds':'Wee!' }  
        log('JS responding with', data)  
        responseCallback(data)  
    })  
  
    bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {  
        log('ObjC called testJavascriptHandler with', data)  
        var responseData = { 'Javascript Says':'Right back atcha!' }  
        log('JS responding with', responseData)  
        responseCallback(responseData)  
    })  
  
    var button = document.getElementById('buttons').appendChild(document.createElement('button'))  
    button.innerHTML = 'Send message to ObjC'  
    button.onclick = function(e) {  
        e.preventDefault()  
        var data = 'Hello from JS button'  
        log('JS sending message', data)  
        bridge.send(data, function(responseData) {  
            log('JS got response', responseData)  
        })  
    }  
  
    document.body.appendChild(document.createElement('br'))  
  
    var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button'))  
    callbackButton.innerHTML = 'Fire testObjcCallback'  
    callbackButton.onclick = function(e) {  
        e.preventDefault()  
        log('JS calling handler "testObjcCallback"')  
        bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {  
            log('JS got response', response)  
        })  
    }  
})  

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

OVGap

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

;(function() {  
  
var require, define;  
  
(function () {  
    var modules = {},  
    // Stack of moduleIds currently being built.  
        requireStack = [],  
    // Map of module ID -> index into requireStack of modules currently being built.  
        inProgressModules = {},  
        SEPERATOR = ".";  
  
    function build(module) {  
        var factory = module.factory,  
            localRequire = function (id) {  
                var resultantId = id;  
                //Its a relative path, so lop off the last portion and add the id (minus "./")  
                if (id.charAt(0) === ".") {  
                    resultantId = module.id.slice(0, module.id.lastIndexOf(SEPERATOR)) + SEPERATOR + id.slice(2);  
                }  
                return require(resultantId);  
            };  
        module.exports = {};  
        delete module.factory;  
        factory(localRequire, module.exports, module);  
        return module.exports;  
    }  
  
    require = function (id) {  
        if (!modules[id]) {  
            throw "module " + id + " not found";  
        } else if (id in inProgressModules) {  
            var cycle = requireStack.slice(inProgressModules[id]).join('->') + '->' + id;  
            throw "Cycle in require graph: " + cycle;  
        }  
        if (modules[id].factory) {  
            try {  
                inProgressModules[id] = requireStack.length;  
                requireStack.push(id);  
                return build(modules[id]);  
            } finally {  
                delete inProgressModules[id];  
                requireStack.pop();  
            }  
        }  
        return modules[id].exports;  
    };  
  
    define = function (id, factory) {  
        if (modules[id]) {  
            throw "module " + id + " already defined";  
        }  
  
        modules[id] = {  
            id: id,  
            factory: factory  
        };  
    };  
  
    define.remove = function (id) {  
        delete modules[id];  
    };  
  
    define.moduleMap = modules;  
})();  
  
  
  
define("ov_gap", function(require, exports, module) {  
  
var ovGap = {  
    callbackId: Math.floor(Math.random() * 2000000000),  
    callbacks: {},  
    commandQueue: [],  
    groupId: Math.floor(Math.random() * 300),  
    groups: {},  
    listeners: {},  
    invoke: function(cmd, params, onSuccess, onFail) {  
        if(!cmd) cmd = "defaultCommand";  
        if(!params) params = {};  
        this.callbackId ++;  
        this.callbacks[this.callbackId] = {  
            success: onSuccess,  
            fail: onFail  
        };  
        var rurl = "ovgap://" + cmd + "/" + JSON.stringify(params) + "/" + this.callbackId;  
        document.location = rurl;  
    },   
    dispatchCommand: function(cmd, params, onSuccess, onFail) {  
        if(!cmd) cmd = "defaultCommand";  
        if(!params) params = {};  
        this.callbackId ++;  
        this.callbacks[this.callbackId] = {  
            success: onSuccess,  
            fail: onFail  
        };  
        var command = cmd + "/" + JSON.stringify(params) + "/" + this.callbackId;  
        this.commandQueue.push(command);  
    },  
    fetchNativeCommands: function() {  
        var json = JSON.stringify(this.commandQueue);  
        this.commandQueue = [];  
        return json;  
    },  
    activate: function() {  
        document.location = "ovgap://ready";  
    },  
    // return group ID  
    createGroup: function() {  
        this.groupId ++;  
        this.groups[this.groupId] = [];  
        return this.groupId;  
    },  
    dispatchCommandInGroup: function(cmd, params, onSuccess, onFail, groupId) {  
        if (!this.groups[groupId]) return false;  
  
        if(!cmd) cmd = "defaultCommand";  
        if(!params) params = {};  
        this.callbackId ++;  
        this.callbacks[this.callbackId] = {  
            success: onSuccess,  
            fail: onFail  
        };  
        var command = cmd + "/" + JSON.stringify(params) + "/" + this.callbackId;  
        this.groups[groupId].push(command);  
        return true;  
    },  
    activateGroup: function(groupId) {  
        if (!this.groups[groupId]) return false;  
        document.location = "ovgap://group/" + groupId;  
    },  
    fetchNativeGroupCommands: function(groupId) {  
        if (!this.groups[groupId]) return [];  
        var json = JSON.stringify(this.groups[groupId]);  
        this.groups[groupId] = [];  
        return json;  
    },  
    callbackSuccess: function(callbackId, params) {  
        try {  
            ovGap.callbackFromNative(callbackId, params, true);  
        } catch (e) {  
            console.log("Error in error callback: " + callbackId + " = " + e);  
        }  
    },  
    callbackError: function(callbackId, params) {  
        try {  
            ovGap.callbackFromNative(callbackId, params, false);  
        } catch (e) {  
            console.log("Error in error callback: " + callbackId + " = " + e);  
        }   
    },   
    callbackFromNative: function(callbackId, params, isSuccess) {  
        var callback = this.callbacks[callbackId];  
        if (callback) {  
            if (isSuccess) {  
                callback.success && callback.success(callbackId, params);  
            } else {  
                callback.fail && callback.fail(callbackId, params);  
            }  
            delete ovGap.callbacks[callbackId];  
        };  
    },  
    addGapListener: function(listenId, onSuccess, onFail) {  
        if (!listenId || !onSuccess || !onFail) return;  
        this.listeners[listenId] = {  
            success : onSuccess,   
            fail : onFail  
        };  
    },  
    removeListener: function(listenId) {  
        if (!this.listeners[listenId]) return;  
        this.listeners[listenId] = null;  
    },  
    triggerListenerSuccess: function(listenId, params) {  
        if (!this.listeners[listenId]) return;  
        var listener = this.listeners[listenId];  
        listener.success && listener.success(listenId, params);  
    },  
    triggerListenerFail: function(listenId, params) {  
        if (!this.listeners[listenId]) return;  
        var listener = this.listeners[listenId];  
        listener.fail && listener.fail(listenId, params);  
    }  
};  
  
module.exports = ovGap;  
  
});  
  
window.ov_gap = require("ov_gap");  
  
}) ();  

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

function onButtonClick() {  
 // 下面是我需要处理的事,处理完之后  
 alert('这里我是要处理一些事的,如果有需要的话。');  
   
 // 这里是回调我们前端与后端商量好的方法  
 // activityList是oc中的方法  
  window.ov_gap.invoke("activityList", null, success, fail);  
}  

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

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

于是我又去寻找别的库,有一个叫apache cordova的库,是支持ios,android,wp的,可是太大了,又是英文的,安装也很困难,学习成本太高,于是看了看就放弃了,这么大的库,给我们带来的可不一定是好处多于动坏处啊。

JavaScriptCore

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

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

首先得加入库

#import <JavaScriptCore/JavaScriptCore.h>  

JSContext这个可是关键。

// webView对象  
@property (nonatomic, strong, readonly) UIWebView    *webView;  
@property (nonatomic, strong, readonly) JSContext    *jsContext;  

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

- (void)webViewDidFinishLoad:(UIWebView *)webView {  
  [_activityView stopAnimating];  
  
  [self dismiss];  
  if (_jsContext == nil) {  
    // 1.  
    _jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];  
      
    // 2. 关联打印异常  
    _jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {  
      context.exception = exceptionValue;  
      DDLogVerbose(@"异常信息:%@", exceptionValue);  
    };  
  
    _jsContext[@"activityList"] = ^(NSDictionary *param) {  
      DDLogVerbose(@"%@", param);  
    };  
      
    // Mozilla/5.0 (iPhone; CPU iPhone OS 10_10 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B411  
    id userAgent = [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];  
    DDLogVerbose(@"%@", userAgent);  
  }  
}  

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

<input type="button" value="测试log" οnclick="activityList({'tytyty':'hehe'})" />  

在点击的时候,直接回调是可以的。

写DEMO的过程中,由于 后台并没有提供好HTML5页面的交互来测试,需要自己写,这里是使用apache服务器,在本地创建一个HTML5页面,自己写一些JS来测试的,如果大家不知道怎么写JS,其实是很简单的,上w3cschool看一看,就明白了。

参考:http://blog.csdn.net/woaifen3344/article/details/42742893

相关文章

网友评论

      本文标题:iOS的UIWebView与Javascript之间的交互

      本文链接:https://www.haomeiwen.com/subject/qrijdctx.html