浅谈js和oc交互之UIWebView

因为项目需求驱动需求。折腾了几天,由于之前没有UIWebView与JS交互的经历,并且觉得这次写完之后感觉以后原生的app和js交互会越来越多,特此留下一点文字,方便日后回顾。
在这我用两种方式小谈UIWebView与JS交互,和一种WKWebView和js交互,相关的项目已上传github,有些兴趣的可以在后文中下载查看。

首先说一下UIWebView与JS的交互

初始化UIWebView

UIWebView *web = [[UIWebView alloc]initWithFrame:[UIScreen mainScreen].bounds];

1.这是加载网络连接的形式
   // NSURL *url = [NSURL URLWithString:@"https://baidu.com"];
   // NSURLRequest *request = [NSURLRequest //requestWithURL:url];
   // [web loadRequest:request];
2.加载本地html
//首先创建webView加载本地的html文件
    NSString *path = [[[NSBundle mainBundle] bundlePath]  stringByAppendingPathComponent:@"JSCallOC.html"];
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]];
    [self.webView loadRequest:request];

    [self.view  addSubview:web];
相关的代理方法
- (void)webViewDidStartLoad:(UIWebView *)webView{
  
}
- (void)webViewDidFinishLoad:(UIWebView *)webView{
   
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
   
}
方式一

第一种方式是用JS发起一个响应的URL请求,然后利用UIWebView的代理方法拦截这次请求,然后再做相应的处理。
我写了一个简单的HTML网页和一个btn点击事件用来与原生OC交互,HTML代码如下:

<html>
    <!--描述网页信息-->
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>小黄</title>
        <style>
            .btn{height:40px; width:60%; padding: 0px 30px; background-color: #0071E7; border: solid 1px #0071E7; border-radius:5px; font-size: 1.2em; color: white}
        </style>
        
        <script>
            
            function clear(){
                document.getElementById('mobile').innerHTML = ''
                document.getElementById('name').innerHTML = ''
                document.getElementById('msg').innerHTML = ''
            }
            
            //提供给OC调用JS的方法列表
            function alertMobile() {
                //alert('我是上面的小黄 手机号是:13300001111')
                document.getElementById('mobile').innerHTML = '我是上面的小黄 手机号是:13300001111'
            }

            function alertName(msg) {
                //alert('你好 ' + msg + ', 我也很高兴见到你')
                document.getElementById('name').innerHTML = '你好 ' + msg + ', 我也很高兴见到你'
            }

            function alertSendMsg(num,msg) {
                //alert('这是我的手机号:' + num + ',' + msg + '!!')
                document.getElementById('msg').innerHTML = '这是我的手机号:' + num + ',' + msg + '!!'
            }
        
            //JS调用oc响应方法列表
            function btnClick1() {
                location.href = "rrcc://showMobile"
            }
        
            function btnClick2() {
                location.href = "rrcc://showName_?xiaohuang"
            }
        
            function btnClick3() {
                location.href = "rrcc://showSendNumber_msg_?13300001111&go climbing this weekend"
            }

        </script>
        
        
    </head>

    <!--网页具体内容-->
    <body>
        <br/><br/>

        <div>
            <label>小黄:13300001111</label>
        </div>
        <br/><br/>

        <div id="mobile"></div>
        <br/>
        <div>
            <button class="btn" type="button" onclick="btnClick1()">小红的手机号</button>
        </div>
        <br/>
        <div id="name"></div>
        <br/>
        <div>
            <button class="btn" type="button" onclick="btnClick2()">打电话给小红</button>
        </div>
        <br/>
        <div id="msg"></div>
        <br/>
        <div>
            <button class="btn" type="button" onclick="btnClick3()">发短信给小红</button>
        </div>


    </body>
</html>

然后在项目的控制器中实现UIWebView的代理方法:

#pragma mark - UIWebViewDelegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSLog(@"%@",NSStringFromSelector(_cmd));
    
    //OC调用JS是基于协议拦截实现的 下面是相关操作
    NSString *absolutePath = request.URL.absoluteString;
    
    NSString *scheme = @"rrcc://";
    
    if ([absolutePath hasPrefix:scheme]) {
        NSString *subPath = [absolutePath substringFromIndex:scheme.length];
        
        if ([subPath containsString:@"?"]) {//1个或多个参数
            
            if ([subPath containsString:@"&"]) {//多个参数
                NSArray *components = [subPath componentsSeparatedByString:@"?"];
                
                NSString *methodName = [components firstObject];
                
                methodName = [methodName stringByReplacingOccurrencesOfString:@"_" withString:@":"];
                SEL sel = NSSelectorFromString(methodName);
                
                NSString *parameter = [components lastObject];
                NSArray *params = [parameter componentsSeparatedByString:@"&"];
                
                if (params.count == 2) {
                    if ([self respondsToSelector:sel]) {
                        [self performSelector:sel withObject:[params firstObject] withObject:[params lastObject]];
                    }
                }
                
                
            } else {//1个参数
                NSArray *components = [subPath componentsSeparatedByString:@"?"];
                
                NSString *methodName = [components firstObject];
                methodName = [methodName stringByReplacingOccurrencesOfString:@"_" withString:@":"];
                SEL sel = NSSelectorFromString(methodName);
                
                NSString *parameter = [components lastObject];
                
                if ([self respondsToSelector:sel]) {
                    [self performSelector:sel withObject:parameter];
                }
                
            }
            
        } else {//没有参数
            NSString *methodName = [subPath stringByReplacingOccurrencesOfString:@"_" withString:@":"];
            SEL sel = NSSelectorFromString(methodName);
            
            if ([self respondsToSelector:sel]) {
                [self performSelector:sel];
            }
        }
    }
    
    return YES;
}

注意:1. 通过scheme进行拦截到我们需要的url做出响应的判断。
2.html中需要设置编码,否则中文参数可能会出现编码问题。
3.JS用直接用document.location的方式,这是一种早期的交互方式,现在oc里面有一个javascriptcore的框架,后面在详解。

早期的JS与原生交互的开源库很多都是用得这种方式来实现的,例如:PhoneGap、WebViewJavascriptBridge。关于这种方式调用OC方法,唐巧早期有篇文章有过介绍:关于UIWebView和PhoneGap的总结

方式二

在iOS 7之后,apple添加了一个新的库JavaScriptCore,用来做JS交互,因此JS与原生OC交互也变得简单了许多。
首先导入JavaScriptCore库, 然后在OC中获取JS的上下文

  • JS调OC方法

首先导入头文件

#import <JavaScriptCore/JavaScriptCore.h>

属性中添加

@property (strong, nonatomic) JSContext *context;

JS调用OC方法一般在WebView的代理方法中实现

#pragma mark - UIWebViewDelegate

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    // 以 html title 设置 导航栏 title
    self.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
    
    // 禁用 页面元素选择
    //[webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitUserSelect='none';"];
    
    // 禁用 长按弹出ActionSheet
    //[webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitTouchCallout='none';"];
    
    // Undocumented access to UIWebView's JSContext
    self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    
    // 打印异常
    self.context.exceptionHandler =
    ^(JSContext *context, JSValue *exceptionValue)
    {
        context.exception = exceptionValue;
        NSLog(@"%@", exceptionValue);
    };
    
    // 以 JSExport 协议关联 native 的方法
    self.context[@"native"] = self;
    
    // 以 block 形式关联 JavaScript function
    self.context[@"log"] =
    ^(NSString *str)
    {
        NSLog(@"%@", str);
    };
    
    // 以 block 形式关联 JavaScript function
    self.context[@"alert"] =
    ^(NSString *str)
    {
        UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"msg from js" message:str delegate:nil cancelButtonTitle:@"ok" otherButtonTitles:nil, nil];
        [alert show];
    };
    
    __block typeof(self) weakSelf = self;
    self.context[@"addSubView"] =
    ^(NSString *viewname)
    {
        UIView *view = [[UIView alloc]initWithFrame:CGRectMake(10, 500, 300, 100)];
        view.backgroundColor = [UIColor redColor];
        UISwitch *sw = [[UISwitch alloc]init];
        [view addSubview:sw];
        [weakSelf.view addSubview:view];
    };
    //多参数
    self.context[@"mutiParams"] =
    ^(NSString *a,NSString *b,NSString *c)
    {
        NSLog(@"%@ %@ %@",a,b,c);
    };
    
}

以上只是相关主要代码,详细的请移步github查看


OC调JS

  • 初始化JSContext
  • 加载js文件
  • 在oc中创建JSValue代表JS中的对象,让JS对象去执行方法
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    self.title = @"oc call js";
    self.context = [[JSContext alloc] init];
    [self.context evaluateScript:[self loadJsFile:@"test"]];
    

}
- (NSString *)loadJsFile:(NSString*)fileName
{
    NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:@"js"];
    NSString *jsScript = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    return jsScript;
}
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)sendToJS:(id)sender {
    NSNumber *inputNumber = [NSNumber numberWithInteger:[self.textField.text integerValue]];
    JSValue *function = [self.context objectForKeyedSubscript:@"factorial"];
    JSValue *result = [function callWithArguments:@[inputNumber]];
    self.showLable.text = [NSString stringWithFormat:@"%@", [result toNumber]];
}

以上是关于UIWebView和js的交互。

戳这里:本文的DEMO地址欢迎star,下载。
戳这里:本文的DEMO地址-JavaScriptCore欢迎star,下载。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容