iOS7之后苹果推出了JavaScriptCore框架,让web页面和本地原生应用可以很方便的交互,并且做到iOS和Android的统一。WEB调OC需要用到JSContext,具体可以查阅苹果官方文档。

  

  • JSContext :为JS 的执行提供运行环境,所有的JS执行都必须在JSContext环境中。JSContext也管理JSVirtualMachine中对象的声明周期。没一个JSValue对象都要强引用关联一个JSContext。当与某JSContext对象关联的所有JSValue释放后,JSContext也会释放。
  • JSVirtualMachine:JS是在一个虚拟的环境中执行,而JSVirtualMachine为其提供底层资源。一个JSVirtualMachine实例代表一个独立的JS对象空间,并为其执行提供资源。它通过加锁虚拟机,保证JSVirtualMachine是线程安全的,如果要并发执行JS,就必须创建多个独立的JSVirtualMachine实例,在不同的实例中执行JS。我们一般不用新建JSVirtualMachine对象,因为创建JSContext时,如果我们不提供一个特性的JSVirtualMachine,内部会自动创建一个JSVirtualMachine对象。
  • JSValue:JSValue是通过JSContext返回或者创建的,并没有构造方法。JSValue包含了每一个JS类型的值,通过JSValue可以将OC的类型转换为JS中的类型,也可以将JS中的类型转换为OC的类型。
  • JSManagedValue:主要用途是解决JSValue对象在OC堆上的安全引用问题。把JSValue保存进OC堆对象中是不正确的,很容易引发循环引用,导致JSContext无法释放。这个类主要是将JSValue对象转换位JSManagedValue的API
  • JSExport:JSExport是一个协议类,但是该协议并没有任何属性和方法。使用的时候可以自定义一个协议类,继承自JSExport。无论我们在JSExport里声明的属性、实例方法还是类方法,继承的协议都会自动的提供给任何JS代码。所以我们只需要再自定义的协议类中,添加上属性和方法就可以了。
  • 对应表

 

JS调用OC的方法

在 viewDidLoad 中 获取JS的上下文 

 

- (void)settingWebView {
    self.jsContext = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
        context.exception = exceptionValue; 
    };
    
    __weak NewTicketDetailVC *weakself = self;
    self.jsContext[@"clickImages"] = ^() {     // JS调用OC大图浏览功能
        NSArray *args = [JSContext currentArguments];
        dispatch_async(dispatch_get_main_queue(), ^{   /** block是在子线程执行的,所以一定要回到主线程是刷新UI */
            [weakself.imagesArray removeAllObjects];
            for (int i = 0; i < args.count; i++) {
                JSValue *jsVal = [args objectAtIndex:i];
                if (i == 0) {  // images
                    NSString *imgsString = jsVal.toString;
                    if ([imgsString containsString:@","]) {
                        [weakself.imagesArray addObjectsFromArray:[imgsString componentsSeparatedByString:@","]];
                    } else if (imgsString.length > 0) {
                        [weakself.imagesArray addObject:imgsString];
                    }
                } else {
                    weakself.clickIndex = jsVal.toUInt32;
                }
            }
            
            if (weakself.imagesArray.count > 0 && weakself.imagesArray.count > weakself.clickIndex) {
                /** 大图浏览 */
                SDPhotoBrowser *browser = [[SDPhotoBrowser alloc] init];
                browser.sourceImagesContainerView = weakself.view; // 原图的父控件
                browser.imageCount = weakself.imagesArray.count; // 图片总数
                browser.currentImageIndex = weakself.clickIndex;
                browser.delegate = weakself;
                browser.isZDKImage = YES;
                [browser show];
            }
        });
    };
    
    /** push to order detail */
    self.jsContext[@"pushToOrderDetail"] = ^() {
        dispatch_async(dispatch_get_main_queue(), ^{
            NewOrderInfo *orderInfo = [NewOrderInfo new];
            orderInfo.billno = weakself.zdkTicketModel.billNo;
            UIStoryboard *sb=[UIStoryboard storyboardWithName:@"NewOrder" bundle:[NSBundle mainBundle]];
            NewOrderDetailVC *vc=[sb instantiateViewControllerWithIdentifier:@"NewOrderDetailVC"];
            vc.orderInfo=orderInfo;
            vc.isDiyOrder = NO;
            [weakself.navigationController pushViewController:vc animated:YES];
        });
    };
}

 

 

 

html对应代码:

<script type="text/javascript">
    function webclickImages(images, index) {
        clickImages(images,index);
    }
        
    function webpushtoOrderDetail() {
        pushToOrderDetail();
    }
</script>
    
<body>
    <button type="button" onclick="webclickImages('img1str,img2str,img3str', 1)">click Images</button>
    <button type="button" onclick="webpushtoOrderDetail()”>push to Order Detail</button>    
</body>

 

OC调用JS的方法

 

方法一:

[_webView stringByEvaluatingJavaScriptFromString:@"showAlert('OC调用JS方法')”];

stringByEvaluatingJavaScriptFromString 是一个同步方法,如果JS 方法比较耗时,会造成页面卡顿。尤其是js 弹出alert 的时候。
alert 也会阻塞界面,等待用户响应,而stringByEvaluatingJavaScriptFromString又会等待js执行完毕返回。这就造成了死锁。
官方推荐使用WKWebView的evaluateJavaScript:completionHandler:代替这个方法。
其实我们也有另外一种方式,自定义一个延迟执行alert 的方法来防止阻塞,然后我们调用自定义的alert 方法。同理,耗时较长的js 方法也可以放到setTimeout 中。
 
 
方法二:
[self.jsContext evaluateScript:@"showAlert('OC调用JS方法')"];

 

html代码 :

function showAlert(message) {
    alert(message);
}

 

 

对于有链接的点击事件可以使用WebVIew的代理方法

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    if (navigationType == UIWebViewNavigationTypeLinkClicked) { //判断是否是单击
        NSURL *url = [request URL];
        if([[UIApplication sharedApplication]canOpenURL:url]) {
            
            NSString *urlStr = [NSString stringWithFormat:@"%@", url];
            NSRange range = [urlStr rangeOfString:@"Contact-Customer-Service"]; //如果包含 需要电机的事件的 列表URL的关键字 就push到Ticket列表
            
            if (range.location != NSNotFound) {
                DebugLog(@"------------------------------------->");
            }
        }
        return NO;
    }
    return YES;
}

 

注意:当页面刷新或者跳转新页面,shouldStartLoadWith与webViewDidStartLoad获取的均为上一个页面的JSContext。也可以放到 webviewDisFinishLoad 执行。JSContext是iOS的webView解析script标签生成的对象,如果加载页面的时候,通过webView.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as? JSContext得到JSContext,那么再次解析script的时候就不会再重新生成

 

 

posted on 2015-11-11 14:59  怡情_老二  阅读(220)  评论(0编辑  收藏  举报