OC調用JS
- 首先我們創建一個
WebView
,加載一個本地的HTML
文件
//加載web 本地的一個html文件
NSURL * url = [[NSBundle mainBundle] URLForResource:@"index" withExtension:@"html"];
NSURLRequest * request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
- 在
webview
加載完成時-(void)webViewDidFinishLoad:(UIWebView *)webView
,我們調用html
文件中的頭部標題設置為導航控制器的title
NSString * title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
self.title = title;
-
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
這個方法就是我們在OC中調用JS的核心方法,我們只需要把寫好的js
以字符串的形式傳入webview
就可以執行document.title
這段語法,這段語法返回的標題就會通過這個方法的返回值返回給我們
- 一個簡單的小例子,
html
文件中有一個test
的js
方法,同樣在webview
加載完成時我們以同樣的方法將test()
傳入給webview
,webview
就會執行這個test
方法
-
html
中的代碼
<script>
function test(){
alert("ceshi");
}
</script>
[webView stringByEvaluatingJavaScriptFromString:@"test();"];
- 效果就是在加載完成時彈出一個我們前端開發最常見的
alert
彈框
JS調用OC
- 在JS中調用OC中的方法比較蛋疼,在
webView
中我們可以通過代理方法捕捉網頁的去向,也就是a標簽的href
屬性,或者網頁的location.href
.
- 我們自定義一個我們自己的協議,例如
location.href = 'heihei://ceshi';
當我們點擊js
中的一個按鈕時onclick="ceshi();"
執行location.href
的跳轉,這樣我們在OC中就可以捕捉到request.URL.absoluteString;
這個跳轉的url
- 當我們捕捉到這個
url
只需要判斷下是否是我們自定的協議,如果是我們這個heihei://
協議,就根據url
執行OC中相應的方法,即我們獲得到了heihei://ceshi
,我們就應該執行OC中名字為ceshi
的方法,我們可以根據字符串截取到ceshi
,然后利用方法響應器- (id)performSelector:(SEL)aSelector;
來調用-(void)ceshi{}
- 在來說一下傳參數的調用,在OC中的方法名帶有參數是以
:
的方式拼接的,然而在js
中:
是無法存在于方法名中的,在這里,我們用_
來代替,并且通過?
拼接參數,參數與參數之間以&
隔開,例如一個參數就是這樣heihei://ceshi_?100
,兩個參數就是這樣heihei://ceshi_number2_?100&200
,依次類推。
- 這樣我們開始在OC中對捕捉到
url
進行處理,首先截掉前面我們自定義的協議,然后以?
分割,第一部分就是ceshi_number2_
。
- 然后我們用
:
替換_
就變成了ceshi:number2:
這就獲得到了OC中- (void)ceshi:(NSString *)number1 number2:(NSString *)number2{
的方法名,仔細比對下哦,看看有沒有問題哦!
- 下一步我們處理參數,剛才以
?
分割的字符串的后半部分為100&200
這就是我們需要的參數,我們將這個字符串以&
分割成數組,就獲取到了我們的參數數組
- 最后通過同樣的方式,方法選擇器通過方法名來調用方法
-(id)performSelector:(SEL)aSelector withObject:(id)object;
-(id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
- 但是我們找遍了蘋果
performSelector
方法也就只找到了上面三種,那么現在問題來了,如果傳三個參數,或者四個參數,我們應該如何處理呢。顯然,這樣是有局限性的。
- 所以我們要模仿蘋果自己寫一個
performSelector
方法,寫一個比較通用的,可以多傳參數的方法,這里我們就用到了NSInvocation
這個類,這是一個利用一個NSInvocation
對象包裝一次方法調用(方法調用者、方法名、方法參數、方法返回值)
- 我們自己封裝了一個
- (id)performSelect:(SEL)selector withObjects:(NSArray *)objects
通過傳參數的方法選擇器和參數數組調用方法的方式,代碼如下
-(id)performSelect:(SEL)selector withObjects:(NSArray *)objects{
//方法簽名(方法描述)
NSMethodSignature * signature = [self methodSignatureForSelector:selector];
if (signature == nil) {
//找不到該方法拋出異常
@throw [NSException exceptionWithName:@"牛逼的錯誤" reason:@"方法找不到" userInfo:nil];
}
// NSInvocation : 利用一個NSInvocation對象包裝一次方法調用(方法調用者、方法名、方法參數、方法返回值)
NSInvocation * invocation = [NSInvocation invocationWithMethodSignature:signature];
//設置響應者
invocation.target = self;
//設置方法選擇器
invocation.selector = selector;
//通過循環設置參數
NSInteger paramsCount = signature.numberOfArguments;
paramsCount = MIN(paramsCount, objects.count);
for (NSInteger i = 0; i<paramsCount; i++) {
id object = objects[i];
if ([object isKindOfClass:[NSNull class]]) {
continue;
}
[invocation setArgument:&object atIndex:i+2];
}
//調用方法
[invocation invoke];
//獲取返回值
id returnValue = nil;
//當返回值不是空的時候
if (signature.methodReturnLength) {
[invocation getReturnValue:&returnValue];
}
return returnValue;
}
- 我們捕捉
href
跳轉時webview
的攔截代理方法就可以寫成下面的樣子
//捕獲location.href的跳轉信息
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
//獲取跳轉的URL
NSString * url = request.URL.absoluteString;
//自定義的協議
NSString * scheme = @"heihei://";
//如果url中是我們自定義的協議,實現OC中的方法
if ([url containsString:scheme]) {
//截取自定義協議后面的字符串
NSString * path = [url substringFromIndex:scheme.length];
NSString * method = nil;
NSArray * params = nil;
//字符串是否包含?,判斷是否有參數
if ([path containsString:@"?"]) {
//有參數
//通過?分割,
NSArray * info = [path componentsSeparatedByString:@"?"];
//數組第一個元素就是方法名,將js中的_替換為:轉換為OC中的方法名
method = [[info firstObject] stringByReplacingOccurrencesOfString:@"_" withString:@":"];
//數組第二個元素就為參數字符串
NSString * param = [info lastObject];
//將參數以&分割變為參數數組
params = [param componentsSeparatedByString:@"&"];
}else{
//無參數
//方法名
method = path;
//參數數組
params = @[];
}
NSLog(@"method:%@------params:%@",method,params);
//通過方法簽名來調用方法,因為參數的個數未知,所以我們封裝一個比較通用的方法
[self performSelect:NSSelectorFromString(method) withObjects:params];
return NO;
}
return YES;
}
<script>
function ceshi()
{
location.href = 'heihei://ceshi';
}
function ceshi1()
{
location.href = 'heihei://ceshi_?100';
}
function ceshi2()
{
location.href = 'heihei://ceshi_number2_?100&200';
}
function ceshi3()
{
location.href = 'heihei://ceshi_number2_number3_?100&200&300';
}
</script>
Demo全部代碼
<html>
<!-- 網頁的描述信息 -->
<head>
<meta charset="UTF-8">
<title>HeiheiJSWithOC</title>
<script>
function ceshi()
{
//沒有參數時
location.href = 'heihei://ceshi';
}
function ceshi1()
{
//一個參數時
location.href = 'heihei://ceshi_?100';
}
function ceshi2()
{
//二個參數時
location.href = 'heihei://ceshi_number2_?100&200';
}
function ceshi3()
{
//三個參數時
location.href = 'heihei://ceshi_number2_number3_?100&200&300';
}
function test(){
alert("ceshi");
}
</script>
</head>
<!-- 網頁的具體內容 -->
<body>
<div>電話:10086</div>
<br>
<button style="color:white ;background-color: red; width:100px; height:30px; margin-top:5px" onclick="ceshi();">測試無參數</button>
<br>
<button style="color:white ;background-color: red; width:100px; height:30px; margin-top:5px" onclick="ceshi1();">測試1個參數</button>
<br>
<button style="color:white ;background-color: red; width:100px; height:30px; margin-top:5px" onclick="ceshi2();">測試2個參數</button>
<br>
<button style="color:white ;background-color: red; width:100px; height:30px; margin-top:5px" onclick="ceshi3();">測試3個參數</button>
<br>
<a >百度</a>
</body>
</html>
//
// ViewController.m
// OCWithJS
//
// Created by zzh on 2017/2/17.
// Copyright ? 2017年 zzh. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()<UIWebViewDelegate>
@property (weak, nonatomic) IBOutlet UIWebView *webView;
@end
@implementation ViewController
-(void)viewDidLoad {
[super viewDidLoad];
//加載web 本地的一個html文件
NSURL * url = [[NSBundle mainBundle] URLForResource:@"index" withExtension:@"html"];
NSURLRequest * request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
}
#pragma mark - 不同參數的測試方法
-(void)ceshi{
NSLog(@"%s----無參數", __func__);
}
-(void)ceshi:(NSString *)number1{
NSLog(@"%s----%@---", __func__,number1);
}
-(void)ceshi:(NSString *)number1 number2:(NSString *)number2{
NSLog(@"%s----%@---%@--", __func__,number1,number2);
}
-(void)ceshi:(NSString *)number1 number2:(NSString *)number2 number3:(NSString *)number3{
NSLog(@"%s----%@---%@---%@---", __func__,number1,number2,number3);
}
#pragma mark - UIWebViewDelegate
//捕獲location.href的跳轉信息
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
//獲取跳轉的URL
NSString * url = request.URL.absoluteString;
//自定義的協議
NSString * scheme = @"heihei://";
//如果url中是我們自定義的協議,實現OC中的方法
if ([url containsString:scheme]) {
//截取自定義協議后面的字符串
NSString * path = [url substringFromIndex:scheme.length];
NSString * method = nil;
NSArray * params = nil;
//字符串是否包含?,判斷是否有參數
if ([path containsString:@"?"]) {
//有參數
//通過?分割,
NSArray * info = [path componentsSeparatedByString:@"?"];
//數組第一個元素就是方法名,將js中的_替換為:轉換為OC中的方法名
method = [[info firstObject] stringByReplacingOccurrencesOfString:@"_" withString:@":"];
//數組第二個元素就為參數字符串
NSString * param = [info lastObject];
//將參數以&分割變為參數數組
params = [param componentsSeparatedByString:@"&"];
}else{
//無參數
//方法名
method = path;
//參數數組
params = @[];
}
NSLog(@"method:%@------params:%@",method,params);
//通過方法簽名來調用方法,因為參數的個數未知,所以我們封裝一個比較通用的方法
[self performSelect:NSSelectorFromString(method) withObjects:params];
return NO;
}
return YES;
}
-(void)webViewDidFinishLoad:(UIWebView *)webView{
//oc調用js
NSString * title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
self.title = title;
[webView stringByEvaluatingJavaScriptFromString:@"test();"];
}
#pragma mark - 根據方法簽名調用方法
-(id)performSelect:(SEL)selector withObjects:(NSArray *)objects{
//方法簽名(方法描述)
NSMethodSignature * signature = [self methodSignatureForSelector:selector];
if (signature == nil) {
//找不到該方法拋出異常
@throw [NSException exceptionWithName:@"牛逼的錯誤" reason:@"方法找不到" userInfo:nil];
}
// NSInvocation : 利用一個NSInvocation對象包裝一次方法調用(方法調用者、方法名、方法參數、方法返回值)
NSInvocation * invocation = [NSInvocation invocationWithMethodSignature:signature];
//設置響應者
invocation.target = self;
//設置方法選擇器
invocation.selector = selector;
//通過循環設置參數
NSInteger paramsCount = signature.numberOfArguments;
paramsCount = MIN(paramsCount, objects.count);
for (NSInteger i = 0; i<paramsCount; i++) {
id object = objects[i];
if ([object isKindOfClass:[NSNull class]]) {
continue;
}
[invocation setArgument:&object atIndex:i+2];
}
//調用方法
[invocation invoke];
//獲取返回值
id returnValue = nil;
//當返回值不是空的時候
if (signature.methodReturnLength) {
[invocation getReturnValue:&returnValue];
}
return returnValue;
}
@end