OC · 记——浅谈JS与OC的交互

我们在开发中,经常会用webView来加载网页,有时候,H5那边给到iOS的网页,加载出来后发现,网页原生的导航栏、悬浮的按钮、底部“电脑版”这类文字没有去掉,让iOS自己来简单处理,那应该怎么办呢?这时候就需要通过JS注入到OC中来实现想要的效果。有三种方式:UIWebView、WKWebView、JavaScriptCore,下面一一介绍。

OC · 记——浅谈JS与OC的交互_第1张图片
需要iOS来删除掉的地方
UIWebView

在注入JS之间,我们需要一个桥梁:webView的代理方法。
遵守,然后获取删除3个部分的JS代码,如下所示:

 //一.移除顶部导航栏
 //1.获取你要删除的标签,找到你要删除的标签的父标签/父节点,移除
 var headerTag = document.getElementsByTagName('header')[0];
 
 headerTag.parentNode.removeChild(headerTag);
 
 //二.移除底部悬浮按钮
 var footBtnTag = document.getElementByClassName('footer-btn-fix')[0];
 footerBtnTag.parentNode.removeChild(footerBtnTag);
 
 //三.移除网页 底部布局
 var footerTag = document.getElementByClassName('footer')[0];
 footerTag.parentNode.removeChile(footerTag);

创建webView,加载网页。

- (void)viewDidLoad {
    [super viewDidLoad];
    /*
     创建展示网页的控件
     */
    UIWebView * webV = [[UIWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    webV.delegate = self;
    /*
     把展示网页的控件添加到根视图上面
     */
    [self.view addSubview:webV];
    /*
     使用webview加载网页,http和https的兼容问题,百度一下,添加两个key就可以了
     */
    NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL 
    URLWithString:@"http://m.dianping.com/tuan/deal/5501525"]];
    [webV loadRequest:request];
}

调用webView的代理方法,注入JS,实现想要的效果。

#pragma mark -- 网页加载完成之后会调用的代理方法
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    /*
     由于总共有三处修改,所以要拼接JS代码。
     webView直接提供了JS注入的方法
     */
    NSMutableString * StrM = [NSMutableString string];
    [StrM appendString:@"var headerTag = document.getElementsByTagName('header')[0];headerTag.parentNode.removeChild(headerTag);"];
    [StrM appendString:@"var footerBtnTag = document.getElementsByClassName('footer-btn-fix')[0];footerBtnTag.parentNode.removeChild(footerBtnTag);"];
    [StrM appendString:@"var footTag = document.getElementsByClassName('footer')[0];footTag.parentNode.removeChild(footTag);"];
    [webView stringByEvaluatingJavaScriptFromString:StrM];
}

效果如下,成功清理干净。

OC · 记——浅谈JS与OC的交互_第2张图片
1.gif
WKWebView

iOS8以后,苹果推出了新框架Wekkit,提供了替换UIWebView的组件WKWebView。各种UIWebView的问题没有了,速度更快了,占用内存少了,一句话,WKWebView是App内部加载网页的最佳选择!
先看下WKWebView的特性:
1.在性能、稳定性、功能方面有很大提升(最直观的体现就是加载网页是占用的内存,模拟器加载百度与开源中国网站时,WKWebView占用23M,而UIWebView占用85M);
2.允许JavaScript的Nitro库加载并使用(UIWebView中限制);
支持了更多的HTML5特性;
3.高达60fps的滚动刷新率以及内置手势;
4.将UIWebViewDelegate与UIWebView重构成了14类与3个协议(查看苹果官方文档);
接下来,我要使用WKWebView来实现上面的效果。直接上代码:

#import "ViewController.h"
#import 
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //创建WKWebView
    WKWebView * wkwebV = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    //添加到界面上
    [self.view addSubview:wkwebV];
    wkwebV.navigationDelegate = self;
    
    //加载网页
    NSURL * url = [NSURL URLWithString:@"http://m.dianping.com/tuan/deal/5501525"];
    NSURLRequest * request = [NSURLRequest requestWithURL:url];
    [wkwebV loadRequest:request];
}

#pragma mark--代理方法
//网页即将开始加载时调用:拦截请求
//拦截标签点击主动发送的请求
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    NSString * urlStr = navigationAction.request.URL.absoluteString;
    NSLog(@"urlStr:%@",urlStr);
    
    if ([urlStr isEqualToString:@"hm://www.yaowoya.com"]) {
        NSLog(@"我点击了图片");
        //在此添加跳转的事件方法
        //......
    }
    
    //类似于UIWebView里面的那个返回Yes的功能
    decisionHandler(WKNavigationActionPolicyAllow);
}

//页面加载完成之后调用 : js注入
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
    NSMutableString * StrM = [NSMutableString string];
    [StrM appendString:@"var headerTag = document.getElementsByTagName('header')[0];headerTag.parentNode.removeChild(headerTag);"];
    [StrM appendString:@"var footerBtnTag = document.getElementsByClassName('footer-btn-fix')[0];footerBtnTag.parentNode.removeChild(footerBtnTag);"];
    [StrM appendString:@"var footTag = document.getElementsByClassName('footer')[0];footTag.parentNode.removeChild(footTag);"];
//点击顶部图片实现点击事件
    [StrM appendString:@"var imgTag = document.getElementsByTagName('figure')[0].children[0];imgTag.onclick = function imgClick(){window.location.href='hm://www.yaowoya.com'};"];
    //注入JS方法
    [webView evaluateJavaScript:StrM completionHandler:nil];
}

在使用WKWebView的代理方法decidePolicyForNavigationAction,会遇到一个常见的崩溃问题:
解决办法: 在webView:decidePolicyForNavigationAction:decisionHandler函数里需执行decisionHandler的block

崩溃问题: Terminating app due to uncaught exception 
‘NSInternalInconsistencyException’ reason: 
‘Completion handler passed to - [ViewController webView: 
decidePolicyForNavigationAction: decisionHandler:] was not called’
JavaScriptCore

JavaScriptCorewebkit的一个重要组成部分,主要是对JS进行解析和提供执行环境。它必须依赖于UIWebView或者WKWebView。我通过JavaScriptCore来实现上面的效果,直接上代码:

#import "ViewController.h"
#import 
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    /*
     创建展示网页的控件
     */
    UIWebView * webV = [[UIWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    webV.delegate = self;
    /*
     把展示网页的控件添加到根视图上面
     */
    [self.view addSubview:webV];
    
    /*
     使用webview加载网页
     */
    NSURLRequest * request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://m.dianping.com/tuan/deal/5501525"]];
    [webV loadRequest:request];
    
    //获取上下文
    //1.获取JS上下文
    JSContext * context = [webV valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    //2.准备触发标签点击事件之后要执行的代码块
    context[@"share"] = ^(){
        NSLog(@"我点击了图片标签");
        //
        /*回到主线程刷新UI:在实际开发中,绝对不要在子线程里面刷新UI,不然会出现这种错误:
        This application is modifying the autolayout engine from a 
        background thread after the engine was accessed from the main 
        thread. This can lead to engine corruption and weird crashes.*/

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            TestTableViewController * vc = [TestTableViewController new];
            [self.navigationController pushViewController:vc animated:YES];
        }];
    };
}

#pragma mark -- 网页加载完成之后会调用的代理方法
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    /*
     由于总共有三处修改,所以要拼接JS代码。
     webView直接提供了JS注入的方法
     */
    NSMutableString * StrM = [NSMutableString string];
    [StrM appendString:@"var headerTag = document.getElementsByTagName('header')[0];headerTag.parentNode.removeChild(headerTag);"];
    [StrM appendString:@"var footerBtnTag = document.getElementsByClassName('footer-btn-fix')[0];footerBtnTag.parentNode.removeChild(footerBtnTag);"];
    [StrM appendString:@"var footTag = document.getElementsByClassName('footer')[0];footTag.parentNode.removeChild(footTag);"];
    //给图片点击点击事件:‘share()’就是标签点击时间的标记(类似于自定义的URL)
    [StrM appendString:@"var imgTag = document.getElementsByTagName('figure')[0].children[0];imgTag.onclick = function imgClick(){share()};"];
    
//    [webView stringByEvaluatingJavaScriptFromString:StrM];
    /*
     1.获取JS上下文
     2.JS注入
     */
    JSContext * context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    [context evaluateScript:StrM];
}

你可能感兴趣的:(OC · 记——浅谈JS与OC的交互)