美文网首页
JS 和 OC 交互

JS 和 OC 交互

作者: iOS104 | 来源:发表于2017-03-09 17:48 被阅读45次
    • 之前项目中OC和JS交互一直用的对UIWebview方法拦截方案,根据自定义的url来调用OC的方法.
    • 考虑到性能问题,现在切换WKWebView

    下面主要讲述下从iOS6至今,Native与JavaScript的交互方法

    iOS 6 UIWebview&&JavaScript

    • 1、iOS6原生没有提供js直接调用Objective-C的方式,只能通过UIWebView的UIWebViewDelegate协议
    #pragma mark UIWebViewDelegate
    
    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
       
       NSURL *url = [request URL];
       // hybrid://Smart:3/pushViewController?{}
       if ([url.scheme isEqualToString:@"hybrid"]) {
           [self handleRequest:url];
           return NO;
       }
       
       return YES;
    }
    
    - (void)handleRequest:(NSURL *)url {
       NSString *handlerName = [url.path substringFromIndex:1];
       
       NSString *queryString = [url.query stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
       NSInteger sid = [url.port integerValue];
       NSDictionary *parameters = [queryString tp_objectFromJSONString];
       
       SEL selector = NSSelectorFromString([NSString stringWithFormat:@"%@:parameters:", handlerName]);
       if (![self respondsToSelector:selector]) {
           return;
       }
       
       IMP imp = [self methodForSelector:selector];
       void (*func)(id, SEL,NSInteger, NSDictionary *) = (void *)imp;
       func(self, selector,sid, parameters);
    }
    
    - (void)pushViewController:(NSInteger)sid parameters:(NSDictionary *)parameters
    {
       
    }
    
    • 2、通过自定义方法来做拦截,并在这个方法中,根据url来调用Objective-C方法

    • 3、如果要OC调JS, stringByEvaluatingJavaScriptFromString方法可以将javascript嵌入页面中

    [self.webView stringByEvaluatingJavaScriptFromString:@""];
    

    iOS 7 UIWebview&&JavaScriptCore

    • 1、iOS7中加入了JavaScriptCore.framework框架。把 WebKit 的 JavaScript 引擎用 Objective-C 封装。该框架让Objective-C和JavaScript代码直接的交互变得更加的简单方便。

    • 2、JavaScriptCore中类及协议:

      • JSContext:给JavaScript提供运行的上下文环境
    • JSValue:JavaScript和Objective-C数据和方法的桥梁

    • JSExport:协议,如果采用协议的方法交互,自己定义的协议必须遵守此协议

    • 3、JS -> OC

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        self.title = @"js call oc";
    
        NSString *path = [[[NSBundle mainBundle] bundlePath]  stringByAppendingPathComponent:@"JSCallOC.html"];
        NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]];
        [self.webView loadRequest:request];
    }
    
    #pragma mark - UIWebViewDelegate
    
    - (void)webViewDidFinishLoad:(UIWebView *)webView
    {
        // 以 html title 设置 导航栏 title
        self.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
        // 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);
        };
     
        
        // 以 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];
        };
    }
    
    
    • 4、OC -> JS
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        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;
    }
    
    - (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]];
    }
    

    iOS 8 WKWebView && JavaScript

    • 1、WebKit框架使得开发者可以在原生App中使用Nitro来提高网页的性能和表现,Nitro就是Safari的JavaScript引擎 WKWebView 不支持JavaScriptCore的方式但提供message handler的方式为JavaScript 与Native通信.

    • 2、WKWebView的好处:

      • 1、在性能、稳定性、功能方面有很大提升(加载速度,内存的提升谁用谁知道
      • 2、更多的支持 HTML5 的特性
      • 3、官方宣称的高达60fps的滚动刷新率以及内置手势
      • 4、Safari 相同的 JavaScript 引擎
      • 5、将 UIWebViewDelegate 与 UIWebView 拆分成了14类与3个协议,包含该更细节功能的实现。
    • 在 UIWebView 中,一句简单的webView.stringByEvaluatingJavaScriptFromString() 就可以用 JS 脚本操纵 WebView,
      在 WKWebView 中,我们可能需要用到 WKScriptMessageHandler 这个协议中的

    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
        if ([message.name isEqualToString:@"Native"]) {
            NSLog(@"message.body:%@", message.body);
            //如果是自己定义的协议, 再截取协议中的方法和参数, 判断无误后在这里手动调用oc方法
            NSMutableDictionary *param = [self queryStringToDictionary:message.body];
            NSLog(@"get param:%@",[param description]);
            
            NSString *func = [param objectForKey:@"func"];
            
            //调用本地函数
            if([func isEqualToString:@"alert"])
            {
                [self showMessage:@"来自网页的提示" message:[param objectForKey:@"message"]];
            }
         
        }
    }
    
    - (NSMutableDictionary*)queryStringToDictionary:(NSString*)string {
        NSMutableArray *elements = (NSMutableArray*)[string componentsSeparatedByString:@"&"];
        NSMutableDictionary *retval = [NSMutableDictionary dictionaryWithCapacity:[elements count]];
        for(NSString *e in elements) {
            NSArray *pair = [e componentsSeparatedByString:@"="];
            [retval setObject:[pair objectAtIndex:1] forKey:[pair objectAtIndex:0]];
        }
        return retval;
    }
    
    • 把 JS 脚本注入到 WebView 的途径是初始化一个 WebView,所以你需要在 WebView 初始化之前写好自己的脚本
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.title = @"JS调用WKWebView";
        
        WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
        config.userContentController = [[WKUserContentController alloc] init];
        [config.userContentController addScriptMessageHandler:self name:@"Native"];
        
        self.myWebView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
        self.myWebView.UIDelegate = self;
        [self.view addSubview:self.myWebView];
        
        [self loadHtml:@"JSWKWebView"];
    }
    

    相关文章

      网友评论

          本文标题:JS 和 OC 交互

          本文链接:https://www.haomeiwen.com/subject/tfcggttx.html