陈江川

邮箱:jiangchuanc@gmail.com

native和JavaScript之间的通信

iOS7之前

iOS -> JS

在native中,主要就是通过stringByEvaluatingJavaScriptFromString:方法对JS进行操作,大致步骤:

- (void)setupWebView {
    
    NSURL *url = [NSURL URLWithString:@"http://m.t.17186.cn"];
    NSURLRequest *request = [NSURLRequest requestWithURL: url];
    
   [self.webView loadRequest:request];
}

#pragma mark - <UIWebViewDelegate>
- (void)webViewDidFinishLoad:(UIWebView *)webView{ 
    
    // 对JS进行操作

  // 删除顶部栏
  // DOM操作,通过class获取标签
  NSString *deleteHeadAD = @"document.getElementById('header').remove();";
  [webView stringByEvaluatingJavaScriptFromString:deleteHeadAD];
}

JS -> iOS

在native中,要实现UIWebView的代理方法shouldStartLoadWithRequest: navigationType:

#pragma mark - UIWebViewDelegate

- (BOOL)webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
 navigationType:(UIWebViewNavigationType)navigationType {

    // URL转String
    NSString *requestString = request.URL.absoluteString; // native://openCamera

    // 解析字符串
    NSRange range = [requestString rangeOfString:@"native://"];

    // 不是事先定义的协议头,直接return
    if (range.location == NSNotFound) return YES;

    NSString *method = [requestString substringFromIndex:range.location + range.length];

    // 动态调用openCamera方法
    [self performSelector:NSSelectorFromString(method)];

    return YES;
}


/**
 *  html调用OC中的方法
 */
- (void)openCamera {
    UIImagePickerController *pickerController = [[UIImagePickerController alloc] init];
    pickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
    [self presentViewController:pickerController animated:YES completion:nil];
}

在html中使用DOM操作

<body>
    <button onclick="openCamera();">访问相册</button>

    <script type="text/javascript">
        // 访问native的相册
        function openCamera() {
            window.location.href = "native://openCamera";
        }
    </script>
</body>

iOS7之后

iOS -> JS

在html文件中简单的实现一个函数

function factorial(n) {
    return n * 10;
};

在native中的实现

#pragma mark - Private Methods

- (void)setupJavaScript {

    // 获取index.js文件路径
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"js"];

    NSString *scriptString = [NSString
                              stringWithContentsOfFile:filePath
                              encoding:NSUTF8StringEncoding
                              error:nil];

    self.context = [[JSContext alloc] init];
    
    [self.context evaluateScript:scriptString];

    // 调用JS的函数
    [self setupJSFunction];
}

- (void)setupJSFunction {
    // 获取textField输入,并转成NSNumber类型
    NSNumber *inputNumber = [NSNumber
                             numberWithInteger:[self.textField.text integerValue]];

    // 创建JCValue, factorial为JS函数的名字
    JSValue *function = [self.context objectForKeyedSubscript:@"factorial"];

    // 向函数中传入参数
    JSValue *result = [function callWithArguments:@[inputNumber]];

    // 返回的结果
    NSNumber *number = [result toNumber];

    NSLog(@"number = %@", number);
}

JS -> iOS

JS访问iOS中的方式有三种:
- 通过JSExportAs给JS中调用native的方法起别名
- 直接调用native的方法
- 通过block实现方法

在native中

@protocol WebViewJSExport <JSExport>

// 方式一
JSExportAs
(functionNameForJS /** JavaScript function 的别名 */,

 - (void)handleFactorialCalculateWithString:(NSString *)string

 );

// 方式二
- (void)pushToNextViewControllerWithTitle:(NSString *)title;

@end

self.context[@"native"] = self;

// 方式三
self.context[@"log"] = ^(NSString *str) {
    NSLog(@"%@", str);
};

在html中,分别对应的调用方式

<!--方式一-->
<a href="#" class="pull-right"  onclick="native.functionNameForJS('忘记密码');">
忘记密码?
</a>

<!--方式二-->
<a href="#" onclick="native.pushToNextViewControllerWithTitle('立即注册');">

<!--方式三-->
<input type="checkbox" onclick="log('下次自动登录');">

文中涉及到的Demo

« Objective-C中的isa指针 OpenGL ES 06-多重纹理 »