UIWebView的基本用法和API不再赘述,直接上重点!
一、iOS7之前交互实现方式
1、OC调用JS
通过UIWebView的stringByEvaluatingJavaScriptFromString方法实现。
2、JS调用OC
简单来说就是URL拦截来实现的;
通过代理方法-(BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType实现,网页中的URL跳转都会在此代理方法中体现,可以获取到即将跳转的URL,判断其中的关键词来决定后续的逻辑;
JS中的实现:(window代表拿到浏览器窗口,规定一个协议发送一个链接,就会响应到webView的这个shouldStartLoadWithRequest代理方法,如下,协议要避开http,类似app间的跳转协议)
functionfn_open_canmera()
{
//调用OC中的openCanmera方法
window.location.href ='toOC://openCanmera';
}
参考文章 。
<span style="font-size:18px;">以上交互方法简单易用,适合轻量的混编开发,相对复杂的需要可以参考开源库<a target=_blank href="https://github.com/marcuswestin/WebViewJavascriptBridge">WebViewJavascriptBridge</a>和<a target=_blank href="https://github.com/dukeland/EasyJSWebView" target="_blank">EasyJSWebView</a>。</span>
二、iOS7之后交互实现方式:JavaScriptCore
关于JavaScriptCore的使用,有很多先行者们已经整理好了,就不在进行重复劳动了,选了一篇认为思路清晰的文章具体介绍请移步。用法介绍挺好的,官方的API介绍也很详细,但是苹果官方没有提供指导手册,也就是没有建议用法,那么问题来了:所有的操作JS对象的前提是获取到JS执行的环境JSContext,API中的获取JSContext的方法有两个
- (instancetype)init;
- (instancetype)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine
这样获取到的JSContext跟UIWebView的对象根本不匹配,如果这样的话,混编的优势和作用就没有那么好了,然而还是被大神们找到了解决方法:
- (void)webViewDidFinishLoad:(UIWebView*)webView{
//搭建环境,首先导入JavaScriptCore库,然后在OC中获取JS的上下文,即该UIWebview的JS执行环境
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//注册一个异常抛出,出错好查
[context setExceptionHandler:^(JSContext*ctx,JSValue*value) {
NSLog(@"error:
%@", value);
}];
通过KCV的方法获取,然而
documentView.webView.mainFrame.javaScriptContext这句神来之笔的代码从何而来,找了诸多文章和博客都是一样的用法,实在心里不透彻!在苹果的苹果iOS开发文档中搜索不到有用资料,只能去苹果Mac开发文档碰碰运气搜索javaScriptContext关键字,
点进去一点一点对应
webView.mainFrame.javaScriptContext可以得到这个属性获取的路径,然后对比Mac 和 iOS的webkit不难发现很多类和接口都是惊人的相似,差不多可以认为iOS是从Mac移植过来的,但是少了很多类和接口。
但是还有
documentView是什么鬼?怎么来的?既然都是私有属性,那就不妨再暴力点借助神器runtime获取更多的信息吧。
unsigned int count = 0;
Ivar *members = class_copyIvarList([web class], &count);
for (int i = 0 ; i < count; i++) {
Ivar var = members[i];
const char *memberName = ivar_getName(var);
const char *memberType = ivar_getTypeEncoding(var);
NSLog(@"%@--%s----%s",[web class], memberName, memberType);
}
free(members);
NSLog(@"********************************************************");
id view_internal = [web valueForKey:@"internal"];
unsigned int count_internal = 0;
Ivar *members_internal = class_copyIvarList([view_internal class], &count_internal);
for (int i = 0 ; i < count_internal; i++) {
Ivar var = members_internal[i];
const char *memberName = ivar_getName(var);
const char *memberType = ivar_getTypeEncoding(var);
NSLog(@"%@--%s----%s",[view_internal class], memberName, memberType);
}
free(members_internal);
NSLog(@"********************************************************");
//由于 UIWebBrowserView 没有webView属性,在父类中查找(此方法只能返回该类声明的属性,不包含父类的)
id view_browserView = [view_internal valueForKey:@"browserView"];
unsigned int count_browserView = 0;
Ivar *members_browserView = class_copyIvarList([view_browserView class], &count_browserView);
for (int i = 0 ; i < count_browserView; i++) {
Ivar var = members_browserView[i];
const char *memberName = ivar_getName(var);
const char *memberType = ivar_getTypeEncoding(var);
NSLog(@"%@--%s----%s",[view_browserView class], memberName, memberType);
}
free(members_browserView);
NSLog(@"********************************************************");
id view_browserView_super = [view_internal valueForKey:@"browserView"];
unsigned int count_DocumentView = 0;
Ivar *members_DocumentView = class_copyIvarList([view_browserView_super superclass], &count_DocumentView);
for (int i = 0 ; i < count_DocumentView; i++) {
Ivar var = members_DocumentView[i];
const char *memberName = ivar_getName(var);
const char *memberType = ivar_getTypeEncoding(var);
NSLog(@"%@--%s----%s",[view_browserView_super superclass], memberName, memberType);
}
free(members_DocumentView);
终于可以在看到UIWebDocumentView了,也看到webview了,但是这个过程如下UIWebView->UIWebViewInternal->UIWebBrowserView->UIWebDocumentView此过程有点对不上,结合上上边的获取顺序UIWebView->UIWebViewInternal,他没有documentView这个属性列表,那么只能在方法列表中了,一言不合就上代码
unsigned int count_Method = 0;
Method *memberFuncs = class_copyMethodList([web class], &count_Method);
for (int i = 0; i < count_Method; i++) {
SEL name = method_getName(memberFuncs[i]);
NSString *methodName = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];
NSLog(@"member method:%@", methodName);
}
终于看到了documentView,此过程结合KVC的属性获取过程即可理解。
到此为止心中疑惑已经解答完毕,然而此种方法有使用私有接口的嫌疑,上架可能要看人品(虽然大家都这么玩);此外还有另外一种解决方法,但是还是有使用私有接口的嫌疑:中文版和英文版。
总之,iOS端还不够成熟和方便,但是可以作为技术储备!