欢迎关注个人博客:tunnycoder.com
在 iOS 开发中很多时候我们会和 WebView 打交道,并且很多应用都采用了 webView 的混合编程技术。
iOS 的 webview 有 2 个类,一个叫UIWebView
,另一个是WKWebView
。两者的基础方法都差不多。
WkWebView 是苹果在 iOS8 中引入的新组件,性能更好,如果不需要乡下兼容 iOS8 以前的版本,可以考虑用 WKWebView 。
iOS7 以前,Objective-C
调用JavaScript
的方式只有一中,就是通过 UIWebView 对象的stringByEvaluatingJavaScriptFromString:
方法。
/**
* 执行一段 JavaScript 代码,并返回字符串类型的返回值
*/
UIWebView *webView = [[UIWebView alloc] init];
// 返回结果 @"2"
NSString *result = [webView stringByEvaluatingJavaScriptFromString:@"1+1"];
// 调用 js 方法
NSString *result2 = [webView stringByEvaluatingJavaScriptFromString:
@"alert('hello js');"];
iOS7 以前,JavaScript
调用Objective-C
的方法有两种:
在 Objective-C 中实现UIWebViewDelegate
代理,实现shouldStartLoadWithRequest
方法,该方法可以监听到 UIWebView 中发出的 URL 请求,通过与 H5 协商一个 URL 通信协议,来拦截指定的 URL ,做相应的操作。
HTML
<div class="suitaqu wrap">
<div class="stq-headerbar blue"><a href="stqnative://back" class="back"><i class="glyphicon glyphicon-menu-left"></i> </a>关于我们</div>
Objective-C
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType
{
NSString *url = request.URL.absoluteString;
NSRange nativeRange = [url rangeOfString:@"stqnative://"];
if (nativeRange.location != NSNotFound) { // url 的协议头是 stqnative://
// do something
// 返回 NO 以阻止 `URL` 的加载或者跳转
// return NO;
}
return YES;
}
在 UIWebView 中, Objective-C
同样可以通过NSHTTPCookieManagerCookiesChangedNotification
事件以监听 cookie 的变化,来做相应的处理。当 JavaScript 修改 document.cookie 后, Objective-C 可以通过分析 cookie 以得到信息.
NSNotificationCenter *center = NSNotificationCenter.defaultCenter;
[defaultCenter addObserverForName:NSHTTPCookieManagerCookiesChangedNotification
object:nil
queue:nil
usingBlock:^(NSNotification *notification) {
NSHTTPCookieStorage *cookieStorage = notification.object;
// do something with cookieStorage
}];
Objective-C
调用JavaScript
代码的时候是同步的JavaScript
调用Objective-C
代码的时候也是同步的- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
iOS7 中引入了JavaScriptCore
, 使得JavaScript
和Objective-C
更容易相互操作。
evaluateScript:
方法就可以执行 JS 代码-(void)webViewDidFinishLoad:(UIWebView *)webView
{
//网页加载完成调用此方法
//获取当前 JS 环境
JSContext *context=[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
NSString *alertJS=@"alert('hello js')"; //准备执行的 js 代码
[context evaluateScript:alertJS];//通过 oc 方法调用 js 的 alert
}
JavaScript 调用 Objective-C 有两种方法:直接调用 JS 和 注入模型
HTML
<input type="button" value="测试 log" onclick="log('测试');" />
Objective-C
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
//网页加载完成调用此方法
//获取当前 JS 环境
JSContext *context=[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//其中 log 是 js 的方法名称,赋给是一个 block 里面是 iOS 代码
//此方法最终将打印出所有接收到的参数
context[@"log"] = ^() {
NSArray *args = [JSContext currentArguments];
for (id obj in args) {
NSLog(@"%@",obj);
}
};
}
此种情况是: HTML 中是通过一个对象来调用 JS 中的方法的,此处就需要用到JSExprot
,只要添加了JSExport
协议的协议,所规定的方法,变量等就会对 JS 开放,我们就可以通过 JS 调用到。
HTML
<a href="#" class="col-xs-6 viewcike" title="2">
<div class="shadow">
<div class="thumb" style="background-image:url( http://xxxxxx.jpeg)"></div>
<div class="bottom">
<h4 class="text-overflow stq-name">音乐喷泉~</h4>
<div class="text-overflow dateandlocation">
<div class="pull-left">大雁塔景区</div>
<div class="pull-right zancount">1</div></div>
<div class="text-overflow dateandlocation">
<div class="pull-left">2016-4-8</div>
<div class="pull-right plcount">0</div>
</div>
</div>
</div>
</a>
JavaScript
i.callNative = function (t, n) {
return console.log("namespace STQN is ", e.STQN), e.STQN || (e.STQN = {
say: function (t) {
console.log("call native action ", t);
var e = JSON.parse(t);
return "authError" === e.action ? i.go("login.html") : void 0
}
}), e.STQN.say(JSON.stringify({action: t, data: n}))
}
$(document).on("click", ".viewcike", function (t) {
t.preventDefault(), STQ.callNative("viewCikeImage", {cikes: i, current: 1 * $(this).attr("title")})
})
注 :稍微解释一下此处,这里用的是jQuery Mobile
框架,点击事件通过 viewcike 这个 class 来进行绑定,执行相应的方法。调用STQ.callNative
方法,一步一步最终调用e.STQN.say(JSON.stringify({action: t, data: n})
因此要在当前 JS 环境中,监听STQN
这个对象并实现say
** PS:如果 js 是一个参数或者没有参数的话 就比较简单,我们的方法名和 js 的方法名保持一致即可;如果 js 是多个参数的话 我们代理方法的所有变量前的名字连起来要和 js 的方法名字一样 ** 方法。 Objective-C 中需要做的就是,创建一个模型并且规定一个 实现了JSExport
协议 的协议,在模型中实现相应的方法。
Objective-C
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
//首先创建一个实现了 JSExport 协议的协议
@protocol STQJSObjectProtocol <JSExport>
JSExportAs
(say /** JSObjectAction 作为 js 方法的别名 */,
- (void)JSObjectAction:(NSString *)message
);
@end
@protocol STQJSObjectDelegate <NSObject>
@optional
/**
* 监听 js 方法
* @param message acton 和 data
*/
- (void)JSObjectToOCWithMessage:(NSDictionary *)message;
@end
@interface STQJSObject : NSObject <STQJSObjectProtocol>
@property (nonatomic, weak) id<STQJSObjectDelegate> delegate;
@end
#import "STQJSObject.h"
@implementation STQJSObject
- (void)JSObjectAction:(NSString *)message
{
//JSON 格式的字符串转成字典
NSDictionary *dict = [STQUtiltiy dictionaryWithJsonString:message];
if ([self.delegate respondsToSelector:@selector(JSObjectToOCWithMessage:)]) {
[self.delegate JSObjectToOCWithMessage:dict];
}
}
//控制器中执行
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
//获取当前 JS 环境
_content = [_detailWebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
// 打印异常,由于 JS 的异常信息是不会在 OC 中被直接打印的,所以我们在这里添加打印异常信息,
_content.exceptionHandler =
^(JSContext *context, JSValue *exceptionValue)
{
context.exception = exceptionValue;
NSLog(@"%@", exceptionValue);
};
//获取 JS 事件
STQJSObject *testJO=[STQJSObject new];
testJO.delegate = self;
_content[@"STQN"]=testJO;
}
#pragma mark - jsModel 代理事件
- (void)JSObjectToOCWithMessage:(NSDictionary *)message
{
//在代理中执行相应的操作,应注意改方法是异步的,不是主线程执行,因此如果需要界面操作,需要 GCD 回主线程操作。
NSLog(@"delegate msg is %@, current is %d", message, [NSThread currentThread].isMainThread);
}
注 :
JSValue
接收。即:STQ.callNative("action" : "viewCikeImage", "callFun":function(string){alert'string'})
参数需要用 JSValue 来接收。*(PS:当然 JSValue 也可以接收普通的键值对儿参数)*JSExportAs
(callNative,
-(void) callNative:(JSValue*)value
);
//在.m 文件中,实现 callNative 方法
-(void) callNative:(JSValue*)value
{
NSString * text = [value valueForProperty:@"action"];//打印"viewCikeImage"
JSValue * func = [value valueForProperty:@"callFun"]; //这里是 JS 参数中的 func;
//调用这个函数
[func callWithArguments:@[@"参数"]];
}
UIWebView 内存占用巨大, iOS8 中苹果给出了一个新的高性能 WebView 解决方案,使用Nitro JavaScript
引擎,性能、稳定性、功能都得到大幅提升。WKWebView
不支持JavaScriptCore
的方式但提供message handler
的方式为JavaScript
与Objective-C
通信.
关于 WKWebView 部分,下次再写~。~
未完~待续 ING...
1
param 2016-07-20 21:34:42 +08:00
我仿佛又听到有人在背后偷偷 @我
|
2
tuimaochang 2016-07-20 22:45:27 +08:00
贴代码的好评!
|
3
tuimaochang 2016-07-20 22:48:37 +08:00
已 rss
|
4
dangyuluo 2016-07-20 23:09:24 +08:00
不错。
|
5
BenX 2016-07-20 23:36:53 +08:00
赞
|
6
eddiechen 2016-07-21 10:06:59 +08:00
赞!
|
7
lwbjing 2016-07-21 10:31:16 +08:00
姿势贴,感谢分享...
|
8
qq2511296 2016-07-21 10:44:09 +08:00
666 不错的文章
|
9
fish19901010 2016-07-21 17:13:42 +08:00
我们现在就是用这个方式自己做 Hybird 。
|
10
jinzhe 2016-07-23 14:39:17 +08:00
WKWebView 有个问题就是用 f7 框架的时候无法 ajax 加载 html
|