GitHub

JS 和OC的交互

在程序开发过程中难免会在App中集成一些网页,我们一般也会采用UIWebView直接加载一段URL将需要展示的网页放在App中比如:

    UIWebView *webView = [[UIWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.view = webView;
    NSURL *url = [NSURL URLWithString:@"https://www.baidu.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [webView loadRequest:request];

还可以在其属性中获得相应的方法,如监听界面是否加载完毕的

@property (nonatomic, readonly, getter=isLoading) BOOL loading;

还有可以设置网页是否够缩放,是否使用内联的播放器播放视频,是否设置视频自动播放等等的一些属性。详细的可以查阅文档看一下。尤其一些代理方法也是非常实用的。除了UIWebView,iOS8.0之后有新增了一类,WKWebView是一个比UIWebView更加方便实用的类,其创建方法如下:

    WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.view = webView;
    NSURL *url = [NSURL URLWithString:@"https://www.baidu.com"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [webView loadRequest:request];

其使用方式和UIWebView没有太大的区别,多出的是内存消耗变少,实用的接口属性增多

// 这是加载网页最常用的一种方式,通过一个网页URL来加载一个WKWebView,这个URL可以是远程的也可以是本地的,例如我加载百度的主页
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;

// 根据一个文件,加载一个WKWebView
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL NS_AVAILABLE(10_11, 9_0);

// 这个方法需要将html文件读取为字符串从而加载为WKWebView,其中baseURL是我们自己设置的一个路径,用于寻找html文件中引用的图片等素材。
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;

// 这个方式使用的比较少,但也更加自由,其中data是文件数据,MIMEType是文件类型,characterEncodingName是编码类型,baseURL是素材资源路径
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL NS_AVAILABLE(10_11, 9_0);

这些便是加载一个网页的前提,下面就是一些交互性的内容,之前在抓包内嵌别人的网页的时候总会有广告,为了适应自己的App需要把这些去掉,所以需要利用Javascript去操作UIWebView的内容,首先呢,可以先用chrome打开要加载的网页,调整为响应设计模式。此时就需要点中你需要隐藏的控件,此时就能够在控制台看到那串标签,以及他所包含的css代码。这样能获取到他的标签就足够了,接下来就可以使用javascript去操作这个控件。首先在代码的加载完成的时候获取到要隐藏的标签代码如下:

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    // 在HTML标签都加载完成后,开始处理HTML标签,调用JS,操作document
    [webView stringByEvaluatingJavaScriptFromString:@"document.getElementById('标签的id').remove();"];
}

如果那个标签是Class那么定位到标签的方法可以改为:

// 获取标签名是class的
document.getElementsByClassName('标签的类名')

如果要去掉的标签是在class的内部可以做相应的调整如下:

// 如果需要隐藏的内容在标签类中是一个数组
document.getElementsByClassName('adpic')[0].style.display = 'none'

上面这种方式是先获取到相应的界面,再根据界面中的标签做相应的处理,即通过Native控制JS将网页中的部分隐藏或显示,还可以设置一些js方法写在Native里面,使用Native直接调用JS的方法,不过这要求会写js代码,简单做个实例如下:

// 自定义editMyLogo函数
[webView stringByEvaluatingJavaScriptFromString:@"var script = document.createElement('script');"
     "script.type = 'text/javascript';"
     "script.text = \"function editMyLogo() { "
     "var logo = document.getElementById('logo');"
     "logo.innerHTML= logo.innerHTML + '展示的名称';"
     "var imglist = logo.getElementsByTagName('IMG');"
     "for (i=0 ; i < imglist.length ; i++ ){"
     "imglist[i].src = 'http://pic.to8to.com/attch/day_160218/20160218_d968438a2434b62ba59dH7q5KEzTS6OH.png';"
     "}"
     "}\";"
     "document.getElementsByTagName('head')[0].appendChild(script);"];

    // 执行editMyLogo函数
    [webView stringByEvaluatingJavaScriptFromString:@"editMyLogo();"];

 其原理是通过stringByEvaluatingJavaScriptFromString将JS函数写进head标签中,然后再调用该函数,其格式就按照上述格式编写即可。还有处理网页请求错误的情况比如说:处理403和404的情况代码如下:

@property (nonatomic, assign) BOOL isPost; // 定义一个变量

// 每一个请求开始发送前都会调用这个方法
// 1, 定义一个全局变量currentRequest,用作保存当前的请求
// 2, 将请求转换成data,然后处理data再将data作为请求数据再次请求
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{

    if (!_isPost) {
        NSHTTPURLResponse *response = nil;
        NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
        if (response.statusCode == 404) {
            // 这里处理 404 代码
        } else if (response.statusCode == 403) {
            // 这里处理 403 代码
        } else {

            _isPost = true;
            [webView loadData:data MIMEType:@"text/html" textEncodingName:@"NSUTF8StringEncoding" baseURL:[request URL]];

        }
        return NO;
    }else{
        NSLog(@"\n\n shouldStartLoadWithRequest请求准备 --  %@ \n\n ",request);
        _isPost = NO;
        return YES;
    }
}

 以上是对相应的已经存在的页面的处理,当要产生一些交互的时候比如说有一个应用场景是在JS中有一个按钮,点击了这个按钮就能调用到Native的分享的方法或者是打电话或者是页面的跳转等等的一些方法这就涉及到具体的OC和JS的交互了,这是JS调用了OC中的方法,具体的思路可以为:首先引入:

#import <JavaScriptCore/JavaScriptCore.h>

接着要加载网页的界面里面设置相应的代理这个代理,代理中的方法就是JS中要调用的方法,如下所示:

@protocol JSObjectDelegate <JSExport>

#pragma mark ----- 测试的JavaScriptCore的数据

// 打电话的方法
- (void)callme:(NSString *)string;
// 分享的方法
- (void)share:(NSString *)shareUrl;
#pragma mark --- 测试H5demo的数据
-(void)webCourseSelection:(NSString * )aString;
-(void)webCourseSelection:(NSString *)tuid coursePrise:(NSString *)coursePrise subjectName:(NSString *)subjectName myteacherName:(NSString *)MyteacherName ;

@end

 遵循UIWebViewDelegate协议和刚刚定义的协议方法,然后在加载完成的方法里面设置相应的关联方式代码如下:

#pragma mark - UIWebViewDelegate

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    //获取html title设置导航栏 title
    self.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
    self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    //捕捉异常回调
    self.context.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
        context.exception = exceptionValue;
        NSLog(@"异常信息: %@",exceptionValue);
    };
    
    //通过JSExport协议关联Native的方法
    self.context[@"Native"] = self;
    
    //通过block形式关联JavaScript中的函数
    __weak typeof(self) weakSelf = self;
   
}

那么在JS中需要配合成固定的格式,才能算关联成功其格式可以参考如下:

<script>
    var alertShowIn = function(str){
        alert(str);

    }
    var callShare = function() {
    var shareUrl = "http://image.baidu.com/search/detail?ct=503316480&z=&tn=baiduimagedetail&ipn=d&ie=utf-8&in=24401&cl=2&lm=-1&st=-1&step_word=&rn=1&cs=&ln=1998&fmq=1402900904181_R&ic=0&s=&se=1&sme=0&tab=&width=&height=&face=0&is=&istype=2&ist=&jit=&fr=ala&ala=1&alatpl=others&pos=1&pn=1&word=图片%20动漫卡通&di=0&os=1199087710,2399135616&pi=0&objurl=http%3A%2F%2Fv.flash.beijingww.com%2Fcomic%2Fwallpaper%2Fjiqimao%2F15.jpg"
    Native.share(shareUrl);
    
    }

    var shareCallBack = function(){
    alert('回调js分享success');
    }
</script>

上述形式的Native.share(shareUrl);这样就能直接与Native中的代理方法share关联,在这个方法里就可以做相应的分享操作即可,其他的方法同理,代理中的方法的参数和Native中的参数一致即可。这样就完成了JS调用OC方法的相关交互。还有就是当OC调用JS中的方法的处理方式是怎样的呢,又有那些区别呢?其应用场景主要表现以上的方式先建立连接,再设置相应的js代码值。就能直接调用。

   -(void)webViewDidFinishLoad:(UIWebView *)webView  
    {  
        //网页加载完成调用此方法  
          
        //首先创建JSContext 对象(此处通过当前webView的键获取到jscontext)  
        JSContext *context=[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];  
        NSString *alertJS=@"alert('test js OC')"; //准备执行的js代码  
        [context evaluateScript:alertJS];//通过oc方法调用js的alert  
          
    }  

 

posted @ 2017-05-22 19:16  windsSunShine  阅读(260)  评论(0编辑  收藏  举报