码迷,mamicode.com
首页 > 移动开发 > 详细

iOS 开发之JS与Native交互

时间:2016-05-12 14:34:31      阅读:371      评论:0      收藏:0      [点我收藏+]

标签:


最近项目中用到了JS与OC交互的,所以我就来讲一下JS与OC交互的详细过程,以及在做项目的时候遇到的问题,跟大家分享一下。

1:关于交互实现方式的选择。

网上讨论比较多的有一个第三方库WebViewJavascriptBridge,个人不建议用,因为本身我们在做H5交互的时候就是给前端增加了工作量,而这种处理方式就需要前端要配置两套代码,一套给安卓,一套给iOS,而且不利于调试。所以我最后选择用系统的JavaScriptCore框架,JavaScriptzCore内部有五个框架,分别是:

   #import"JSContext.h",

   #import"JSValue.h",

   #import"JSManagedValue.h",

   #import"JSVirtualMachine.h",

   #import"JSExport.h"

本文只讲常用的JSContext和JSValue,JSExport这三个类。

(1):JSContext为JavaScript提供运行环境,通过- (JSValue *)evaluateScript:(NSString *)script;这个方法就可以执行一段JS的脚本代码,具体使用方法如下:

NSString *alertJS=@"alert(‘test js OC‘)";//准备执行的js代码

[self.jsContextevaluateScript:alertJS];//通过oc方法调用jsalert

(2):JSValue是JavaScript和Native之间互换的桥梁,它提供了多种方法可以方便地把JavaScript数据类型转换成Objective-C,或者是转换过去。

 Objective-c    JavaScript 

  nil              undefined         //对应的参数转换关系

  NSNull           null

  NSString         string

  NSNumber         number,boolean

  NSDictionary     Object object  

  NSArray          Array object

  NSDate           Date object

  NSBlock          Function object

  id               Wrapper object

  Class            Constructor object

  方法的转换举例:

  1‘)self.jsContext[@"test1"] = ^() {

              NSArray *args = [JSContextcurrentArguments];

              for (id obj in args) {

                   NSLog(@"测试:%@",obj);

              }

   };

  2’)

//调用JS的方法并给JS传参数

     JSValue *jsFunc2 =self.jsContext[@"getId"];//getId是JS里面的方法

     PersonalSetting *s =[[PersonalSettingalloc]init];

     BOOL session_nil = s.session == nil || [s.session isEqualToString:@""];

     BOOL addrID_nil = s.addr ==nil||[s.addrisEqualToString:@""];

     if (addrID_nil ==YES) {

          s.addr_id =@"undefined";

     }

     if (session_nil ==YES){

          s.session =@"undefined";

     }

     [jsFunc2callWithArguments:@[@{@"session": s.session,@"addr":s.addr}]];

  (3):异常处理

      Objective-C的异常会在运行时被Xcode捕获,而在JSContext中执行的JavaScript如果出现异常,只会被JSContext捕获并存储在exception属性上,而不会向外抛出。时时刻刻检查JSContext对象的exception是否不为nil显然是不合适,更合理的方式是给JSContext对象设置exceptionHandler,它接受的是^(JSContext *context, JSValue *exceptionValue)形式的Block。其默认值就是将传入的exceptionValue赋给传入的contextexception属性:

              /******2.关联打印异常,由于JS的异常信息是不会在OC中被直接打印的,所以我们在这里添加打印异常信息*******/ 

      _jsContext.exceptionHandler = ^(JSContext *context,JSValue *exceptionValue) {context.exception = exceptionValue;NSLog(@"异常信息:%@", exceptionValue);};

   (4):开发步骤

       1’:#import<JavaScriptCore/JavaScriptCore.h>  #import<UIKit/UIKit.h>

       2‘:

           @interface H5DetailVC ()<UIWebViewDelegate>

           @property (nonatomic,strong) UIWebView *webView;

           @property (nonatomic,strong) JSContext *jsContext;

           @property (nonatomic,strong) OCModel   *jsocModel;

           @end

       3’:- (UIWebView *)webView{

               if (_webView == nil) {

                      _webView = [[UIWebViewalloc]initWithFrame:CGRectMake(0,0,[UIScreenmainScreen].bounds.size.width,[UIScreenmainScreen].bounds.size.height-64)];

                      _webView.scalesPageToFit = YES;

                      _webView.scrollView.showsVerticalScrollIndicator=NO;

                      _webView.scrollView.showsHorizontalScrollIndicator=NO;

                      _webView.scrollView.bounces=NO;

                      _webView.backgroundColor = f6f6f6Color;

               }return_webView;}

        4‘:

        - (void)initOCModel{

            self.jsContext=[[JSContextalloc]init];

            self.jsocModel =[[OCModelalloc]init];

            //传导航控制器

            self.jsocModel.navigationContro=(NavigationViewController *)self.navigationController;

            //传换地址的参数

            self.jsocModel.ptOrderID=self.ptOrderID;

            self.jsocModel.regionID=self.regionID;

            self.jsocModel.title=self.shareTitle;

            self.jsocModel.content=self.content;

            self.jsocModel.imageURL=self.imageURL;

            self.jsocModel.shareURL=self.shareURL;

           //回调

            __weaktypeof(self)weakSelf = self;

            self.jsocModel.block = ^(CheckOrderControllerCellInfo *cellInfo,NSString *tuan_id,NSString *ptOrderID){

                CheckOrderController *vc=[CheckOrderControllercreateViewController:cellInfo];

                vc.tuan=tuan_id;

                vc.order=ptOrderID;

                __strongtypeof(weakSelf)strongSelf=weakSelf;

                dispatch_async(dispatch_get_main_queue(), ^{

                    [strongSelf.navigationControllerpushViewController:vcanimated:YES];

                });

            };

}

            - (void)dealloc{

               NSLog(@"回收");

            }

            - (void)loadRequest{

                NSURL *url = [NSURLURLWithString:self.h5Url];

                NSURLRequest *request = [NSURLRequestrequestWithURL:url];

               [_webViewloadRequest:request];

            }

            - (void)backVC{

                if ([_webViewcanGoBack]) {

                    [_webViewgoBack];

                }else{

                    [self.navigationControllerpopViewControllerAnimated:YES];

                }

}

            - (void)setID{

               JSValue *jsFunc2 =self.jsContext[@"getId"];

               PersonalSetting *s =[[PersonalSettingalloc]init];

               BOOL session_nil = s.session ==nil || [s.session isEqualToString:@""];

                BOOL addrID_nil = s.addr ==nil||[s.addr isEqualToString:@""];

                if (addrID_nil == YES) {

                   s.addr =@"undefined";

                }

                if (session_nil == YES){

                    s.session = @"undefined";

                }

                [jsFunc2callWithArguments:@[@{@"session": s.session,@"addr":s.addr_id}]];

}

                #pragma mark - UIWebViewDelegate

                - (void)webViewDidFinishLoad:(UIWebView *)webView{

                   /*************************************** 1.初始化content**********************************/

                    _jsContext = [webViewvalueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

                   /******2.关联打印异常,由于JS的异常信息是不会在OC中被直接打印的,所以我们在这里添加打印异常信息*******/

                    _jsContext.exceptionHandler = ^(JSContext *context,JSValue *exceptionValue) {

                    context.exception = exceptionValue;

                    NSLog(@"异常信息:%@", exceptionValue);

                    };

                   /***************************************3.初始化JSOCModel********************************/

                    self.jsContext[@"OCModel"] = self.jsocModel;

                    self.jsocModel.jsContext   = self.jsContext;

                    self.jsocModel.webView     = self.webView;

                   //    NSString *alertJS=@"alert(‘test js OC‘)"; //准备执行的js代码

                    //    [self.jsContext evaluateScript:alertJS];//通过oc方法调用jsalert

                }

                - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{

                     [MBProgressHUDshowError:@"加载失败"];

                }

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

                     returnYES;

                }


           (6):OCMode内部的实现

#import <Foundation/Foundation.h>

#import <JavaScriptCore/JavaScriptCore.h>

#import <UIKit/UIKit.h>


typedef void(^pushCheckOrderVCBlock)(CheckOrderControllerCellInfo *cellInfo,NSString *tuan_id,NSString *ptOrderID);

@protocol JavaScriptObjectiveCDelegate <JSExport>

//跳转到登录页

- (void)pushToLoginViewController;

//获得ID

- (void)getiOSID;

//跳转到地址选择页

- (void)pushToChooseAddressVCWithParams:(NSDictionary *)params;

//分享团链接

- (void)shareTuanMethod;

//跳转到结算页

- (void)pushToCheckOrderVCWithDictonry:(NSDictionary *)params Dictionry2:(NSDictionary *)param2;

//跳转到首页的方法

- (void)pushToRootVC;

//跳转到五个人头页面

- (void)pushToFightGroupDetailVCWithPtOrderID:(NSString *)ptOrderID;

//跳转到店铺的方法

- (void)pushToShopVCWithShopID:(NSString *)shopID;

//分享红包页面

- (void)shareRedPaper;

@end



@interface OCModel :NSObject<JavaScriptObjectiveCDelegate>

@property (nonatomic,copy)pushCheckOrderVCBlock block;

@property (nonatomic,weak)JSContext *jsContext;

@property (nonatomic,weak)UIWebView *webView;

//分享的参数

@property (nonatomic,strong)NSString *title;

@property (nonatomic,strong)NSString *content;

@property (nonatomic,strong)NSString *imageURL;

@property (nonatomic,strong)NSString *shareURL;

//换地址参数

@property (nonatomic,strong)NSString *ptOrderID;

@property (nonatomic,strong)NSString *regionID;

@property (nonatomic,strong)NavigationViewController *navigationContro;

@end

//

//  OCModel.m

//  XXX

//

//  Created by lirong on 16/4/20.

//  Copyright ? 2016 daming. All rights reserved.

//


#import "OCModel.h"

#import "ShareView.h"

#import "SubjectShopController.h"

@implementation OCModel

#pragma mark -登录

- (void)pushToLoginViewController{

    LoginViewController *vc=[[LoginViewControlleralloc]init];

    [self.navigationContro pushViewController:vcanimated:YES];

}

- (void)getiOSID{

    JSValue *jsFunc2 = self.jsContext[@"getId"];

    PersonalSetting *s =[[PersonalSettingalloc]init];

    BOOL session_nil = s.session ==nil || [s.session isEqualToString:@""];

    BOOL addrID_nil = s.addr ==nil||[s.addr isEqualToString:@""];

    if (addrID_nil == YES) {

        s.addr = @"undefined";

    }

    if (session_nil == YES){

        s.session = @"undefined";

    }

    [jsFunc2 callWithArguments:@[@{@"session": s.session,@"addr":s.addr}]];

}

#pragma mark -选地址

- (void)pushToChooseAddressVCWithParams:(NSDictionary *)params{

    PersonalSetting *s=[PersonalSettinggetInstance];

    s.send = [params objectForKey:@"send"];

    s.singleOrGroup = [params objectForKey:@"type"];//"TUAN" "SINGLE"

    NSString *Order=[params objectForKey:@"order"];

    NSString *tuan=[params objectForKey:@"tuan"];

    s.tuan = tuan;

    LocateAndChooseAddressControllerParam *param = [[LocateAndChooseAddressControllerParamalloc]init];

    param.from =@"去结算页";

    param.pt_order_id = Order;

    LocateAndChooseAddressController *vc= [LocateAndChooseAddressControllercreateViewController:param];

    __weak typeof(self)weakSelf=self;

    dispatch_async(dispatch_get_main_queue(), ^{

        [weakSelf.navigationContro pushViewController:vc animated:YES];

    });

//    [self.navigationContro pushViewController:vc animated:YES];

}

#pragma mark -分享

- (void)shareTuanMethod{

    [TalkingDatatrackEvent:@"呼唤小伙伴"];

    PersonalSetting *s=[PersonalSettinggetInstance];

    s.paySuccess = YES;

    ShareView *shareV=[ShareViewdefaultShareView];

    NSData *data=[NSDatadataWithContentsOfURL:[NSURLURLWithString:self.imageURL]];

    UIImage *image=[UIImageimageWithData:data];

    [shareV setShareContentWithTitle:self.title content:self.content title2:@"" content2:@"" shareImage:imageshareURL:self.shareURL];

    [shareV show];

    [shareV setShareViewBlock:^(BOOL isSuccess) {

        if (isSuccess) {

            DLog(@"分享成功");

        }else {

            DLog(@"分享失败");

        }

    }];

    s.paySuccess = NO;

}

#pragma mark checkout成功去结算页

- (void)pushToCheckOrderVCWithDictonry:(NSDictionary *)params Dictionry2:(NSDictionary *)param2{

    DLog(@"param2:%@",param2);

    DLog(@"params:%@",params);

    PersonalSetting *s=[PersonalSettinggetInstance];

    s.send = [param2 objectForKey:@"send"];

    s.singleOrGroup = [param2 objectForKey:@"type"];

    NSString *Order=[param2 objectForKey:@"order"];

    CheckOrderControllerCellInfo*cellInfo=[[CheckOrderControllerCellInfoalloc]init];

    NSString *limit=[[params objectForKey:@"limit"] stringValue];

    //购物车

    NSMutableArray *cart=[params objectForKey:@"cart"];

    NSDictionary *cartDict=[[NSDictionaryalloc]init];

    NSMutableArray *image=[[NSMutableArrayalloc]init];//商品图片数组

    NSString *price=@"";

    NSString *status=@"";

    NSString *title1=@"";

    NSString *tuan_id=@"";

    NSString *type=@"";

    NSString *shopName=@"";

    if (cart.count>0) {

        cartDict=[cart objectAtIndex:0];

        image=[cartDict objectForKey:@"image"];

        price=[cartDict objectForKey:@"price"];

        status=[cartDict objectForKey:@"status"];

        title1=[cartDict objectForKey:@"title1"];

        tuan=[cartDict objectForKey:@"tuan"];

        type=[cartDict objectForKey:@"type"];

        shopName=[cartDict objectForKey:@"shopName"];

    }

    NSString *cart_title=[params objectForKey:@"cart_title"];

//    NSString *coupon=[params objectForKey:@"coupon"];

//    NSString *coupon_disc=[params objectForKey:@"coupon_disc"];

    NSMutableArray *coupon_list=[params objectForKey:@"coupon_list"];//优惠券

    NSString *due=[params objectForKey:@"due"];

    NSString *inventory=[[params objectForKey:@"inventory"] stringValue];

    

    NSString *cre = [params objectForKey:@"cre"];

    NSString *useCre = [params objectForKey:@"use_cre"];

    //给结算页传值

    cellInfo.title=title1;

    cellInfo.credit = credit;

    cellInfo.use_credit = useCredit;

    cellInfo.groupNumber=[NSStringstringWithFormat:@"%@",type];

    cellInfo.price=price;

    cellInfo.due=due;

    cellInfo.cart_title = cart_title;

    cellInfo.couponList = [[NSMutableArrayalloc]init];

    for (int i=0;i<coupon_list.count;i++){

        SkuCheckoutModel *model=[[CheckoutModelalloc]init];

        model.id            = coupon_list[i][@"id"];

        model.cash          = coupon_list[i][@"cash"];

        model.valid         = coupon_list[i][@"valid"];

        model.coupon_status = coupon_list[i][@"coupon_status"];

        model.msg1          = coupon_list[i][@"msg1"];

        model.msg2          = coupon_list[i][@"msg2"];

        model.threshold     = coupon_list[i][@"threshold"];

        [cellInfo.couponList addObject:model];//优惠券

    }

    if (buy_limit !=nil&&![buy isEqualToString:@""]) {

        cellInfo.limitNum=[buy intValue];

    }

    if (inventory !=nil&&![inventoryisEqualToString:@""]) {

        cellInfo.stockQuantity=[inventory intValue];

    }

    cellInfo.shopName=shopName;

    if (_block) {

        _block(cellInfo,tuan_id,ptOrderID);

    }

}

#pragma mark -回首页

- (void)pushToRootVC{

    [self.navigationContropopToRootViewControllerAnimated:YES];

}

#pragma mark -跳转到XXX页面

- (void)pushToFightGroupDetailVCWithPtOrderID:(NSString *)ptOrderID{

    DetailViewController *vc=[[DetailViewController alloc]init];

    vc.order=ptOrderID;

    __weak typeof(self)weakSelf=self;

    dispatch_async(dispatch_get_main_queue(), ^{

        [weakSelf.navigationContro pushViewController:vc animated:YES];

    });

//    [self.navigationContro pushViewController:vc animated:YES];

}

#pragma mark -跳转到店铺页面

- (void)pushToShopVCWithShopID:(NSString *)shopID{

    SubjectShopController *vc=[[SubjectShopControlleralloc]init];

    vc.supplier_id=shopID;

    __weak typeof(self)weakSelf=self;

    dispatch_async(dispatch_get_main_queue(), ^{

        [weakSelf.navigationContro pushViewController:vc animated:YES];

    });

//    [self.navigationContro pushViewController:vc animated:YES];

}

- (void)showAlert:(NSString *)title msg:(NSString *)msg {

    dispatch_async(dispatch_get_main_queue(), ^{

        UIAlertView *a = [[UIAlertViewalloc]initWithTitle:titlemessage:msgdelegate:nilcancelButtonTitle:@"Ok"otherButtonTitles:nil,nil];

        [a show];

    });

}

#pragma mark - 确认收货发红包

- (void)shareRedPaper{

    PersonalSetting *s=[PersonalSettinggetInstance];

    s.paySuccess = YES;

    ShareView *shareV=[ShareViewdefaultShareView];

//    NSData *data=[NSData dataWithContentsOfURL:[NSURL URLWithString:self.imageURL]];

//    UIImage *image=[UIImage imageWithData:data];

    [shareV setShareContentWithTitle:self.titlecontent:self.contenttitle2:@""content2:@""shareImage:nilshareURL:self.shareURL];

    [shareV show];

    [shareV setShareViewBlock:^(BOOL isSuccess) {

        if (isSuccess) {

            DLog(@"分享成功");

        }else {

            DLog(@"分享失败");

        }

    }];

    s.paySuccess = NO;

}

@end


(7):webView 多次push页面会造成crash,一般都是crash在webView的线程WebThread里。

这种情况是因为主线程被卡住了。主线程被卡住是非常常见的场景,具体表现就是程序不响应任何的UI交互。所以我们在webView里面进行push操作的时候,一定要在主线程里面进行。

__strongtypeof(weakSelf)strongSelf=weakSelf;

                dispatch_async(dispatch_get_main_queue(), ^{

                    [strongSelf.navigationControllerpushViewController:vcanimated:YES];

 });


4:JS端的写法:(举例说明)

<!DOCTYPE html>

<html lang="en">

<head>

    <meta charset="UTF-8">

    <title>Title</title>

</head>

<body>

        <script>

            var isLogin = false;

            var session_id ;

            var addr_id = "";

            var device;


            function _isIoS(){

                var ua = navigator.userAgent.toLowerCase();

                if(ua.match(/iPhone\sOS/i) == ‘iphone os‘){

                    return true;

                }else{

                    return false;

                }

            }


            function _isAndroid(){

                var ua = navigator.userAgent.toLowerCase();

                var msgss = navigator.userAgent

                if(ua.match(/Android/i) == ‘android‘){

                    return true;

                }else{

                    return false;

                }

            }


            if(_isAndroid()){

                device = ‘android‘;

            }else if(_isIoS()){

                device = ‘ios‘;

            }



            function toLogin(){

                if(device==‘android‘){

                    PtUbossJsInterface.toLogin()

                }else if(device == ‘ios‘){

                    OCModel.pushToLoginViewController()

                }else{


                }

            }

            function getLoginSession_id(){

                if(device==‘android‘){

                    PtUbossJsInterface.getLoginSession_id(setIsLogin);

                }else if(device == ‘ios‘){

                    OCModel.getSessionID()

                }else{


                }

            }

            function toAddrList(){

                if(device==‘android‘){

                    PtUbossJsInterface.toAddrList()

                }else if(device == ‘ios‘){

                    OCModel.pushToChooseAddressVCWithParams({})

                }else{


                }

            }

            function getAddrId(){

                if(device==‘android‘){

                    PtUbossJsInterface.getAddrId(setAddrId)

                }else if(device == ‘ios‘){

                    OCModel.getAddressID()

                }else{


                }

            }

            function shareTuanLink(){

                if(device==‘android‘){

                    PtUbossJsInterface.shareTuanLink(‘c‘,‘d‘,‘d‘,‘dfs‘)

                }else if(device == ‘ios‘){

                    OCModel.shareTuanMethodShareURLTitleContentImageURL(‘a‘,‘b‘,‘c‘,‘d‘)

                }else{


                }

            }

            function toPtCheckout(){

                if(device==‘android‘){

                    PtUbossJsInterface.toPtCheckout()

                }else if(device == ‘ios‘){

                    OCModel.pushToCheckOrderVCWithDictonry()

                }else{


                }

            }

            function toFirstActivity(){

                if(device==‘android‘){

                    PtUbossJsInterface.toFirstActivity()

                }else if(device == ‘ios‘){

                    OCModel.pushToRootVC()

                }else{


                }

            }

            function toShop(){

                if(device==‘android‘){

                    PtUbossJsInterface.toShop()

                }else if(device == ‘ios‘){

                    OCModel.pushToShopVCWithShopID(‘shop_id‘)

                }else{

                    

                }

            }

            function toPtDetail(){

                if(device==‘android‘){

                    PtUbossJsInterface.toPtDetail(‘pt11604221104zmlow3‘);

                }else if(device == ‘ios‘){

                    OCModel.pushToFightGroupDetailVCWithPtOrderID(‘pt11604221104zmlow3‘);

                }else{


                }

            }



        </script>


        <p id="id_device">当前的手机机型</p>


        <div>

            <article id="id_login">登入状态</article>

        <button onclick="toLogin()">到登入页</button>

        <button onclick="getLoginSession_id()">获取登入状态</button>

        </div>

        <br>

        <!--**********************************************************************-->


        <div>

            <article id="id_addr_id">当前选中的地址id</article>

        <button onclick="toAddrList()">跳转到地址选择页面</button>

        <button onclick="getAddrId()">获取当前选中地址的id</button>


        </div>

        <br>

        <!--**********************************************************************-->


        <div>

            <article></article>

        <button onclick="shareTuanLink()">分享团链接的方法</button>

        </div>

        <br>

        <!--**********************************************************************-->


        <div>

            <article></article>

        <button onclick="toPtCheckout()">到结算页的方法</button>

        </div>

        <br>

        <!--**********************************************************************-->


        <div>

            <article></article>

        <button onclick="toFirstActivity()">跳回首页的方法</button>

        </div>

        <br>

        <!--**********************************************************************-->


        <div>

            <article></article>

        <button onclick="toShop()">跳转到店铺的方法</button>

        </div>

        <br>

        <!--**********************************************************************-->


        <div>

            <article></article>

        <button onclick="toPtDetail()">到详情页的方法</button>

        </div>

        <br>

        <!--**********************************************************************-->


        <script>


            function setIsLogin(s){

                session_id = s;

                alert(s);

                if(session_id){

                    document.getElementById(‘id_login‘).innerHTML =用户已登入+session_id;

                }else{

                    document.getElementById(‘id_login‘).innerHTML =用户未登入

                }

            }


            document.getElementById(‘id_device‘).innerHTML="当前设备是"+device;


            if(isLogin){

                document.getElementById(‘id_login‘).innerHTML =用户已登入

            }else{

                document.getElementById(‘id_login‘).innerHTML =用户未登入

            }

            function setAddrId(id){

                addr_id = id;

                document.getElementById(‘id_addr_id‘).innerHTML = addr_id;

            }

        </script>


</body>

</html>




iOS 开发之JS与Native交互

标签:

原文地址:http://blog.csdn.net/darongzi1314/article/details/51361221

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!