UIWebView和原生交互 JavaScriptCore

UIWebView的基本用法和API不再赘述,直接上重点!

一、iOS7之前交互实现方式

1、OC调用JS

通过UIWebViewstringByEvaluatingJavaScriptFromString方法实现。

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端还不够成熟和方便,但是可以作为技术储备!


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值