IOS——UIWebView基本使用与JS交互

###一、UIWebView的几种加载方式html

加载本地的HTML文件前端

  • 经过NSURLRequest加载:
//建立URL
    NSURL *url = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"html"];
    //建立NSURLRequest
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    //加载
    [_webView loadRequest:request];
  • 经过NSString加载:

UIWebView 还支持将一个NSString对象做为源来加载。你能够为其提供一个基础URL,来指导UIWebView对象如何跟随连接和加载远程资源:java

NSString *path = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
NSString *htmlString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:path]];

###二、UIWebView代理方法git

设置代理github

self.webView.delegate = self;

代理方法web

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    //返回YES,进行加载。经过UIWebViewNavigationType能够获得请求发起的缘由
    return YES;
}

开始加载json

- (void)webViewDidStartLoad:(UIWebView *)webView
{
    
}

完成加载数组

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

加载出错浏览器

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

###三、导航框架

UIWebView 类内部会管理浏览器的导航动做

[webView goBack]; //后退   
[webView goForward]; //前进   
[webView reload];//重载    
[webView stopLoading];//取消载入内容

###四、UIWebView和JavaScript交互

UIWebView的方法

- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

相关应用:

// 获取当前页面的title
NSString *title = [webview stringByEvaluatingJavaScriptFromString:@"document.title"];

// 获取当前页面的url
NSString *url = [webview stringByEvaluatingJavaScriptFromString:@"document.location.href"];

iOS原生应用和web页面的交互大体上有这几种方法iOS7以后的JavaScriptCore、拦截协议、第三方框架WebViewJavaScriptBridge、iOS8以后的WKWebView 在这里只介绍JavaScriptCore和'拦截协议'

iOS7以后苹果推出了JavaScriptCore这个框架,从而让web页面和本地原生应用交互起来很是方便,并且使用此框架能够作到Android那边和iOS相对统一,web前端写一套代码就能够适配客户端的两个平台,从而减小了web前端的工做量。

####4.1 JavaScriptCore

JavaScriptCore中类及协议:

JSContext:给JavaScript提供运行的上下文环境 JSValue:JavaScript和Objective-C数据和方法的桥梁 JSManagedValue:管理数据和方法的类 JSVirtualMachine:处理线程相关,使用较少 JSExport:这是一个协议,若是采用协议的方法交互,本身定义的协议必须遵照此协议

web前端

在三端交互中,web前端要强势一些,一切传值、方法命名都按web前端开发人员来定义,让另外两端去作适配。在这里以调用NSLog和保存图片为例来详细讲解,测试网页代码取名为test.html,其代码内容以下:

test.html代码内容

<!DOCTYPE html>
<html>
    <head lang="en">
        <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
            </head>
    <body>
        <div style="text-align: center;">
            <h1>Objective-C和JavaScript交互的那些事</h1>
            <div><img src="http://chuantu.biz/t5/34/1474337388x3063167327.jpg" /></div>
            <div><input type="button" value="点击我能够看到Xcode的NSLog" onclick="Toyun.callCamera()" /></div>
            <div><input type="button" value="点击我能够保持图片到手机相册" onclick="callShare()" /></div>
        </div>
        <script>
            var callShare = function() {
                var shareInfo = JSON.stringify({"title": "标题", "desc": "内容", "shareUrl": "http://chuantu.biz/t5/34/1474337388x3063167327.jpg"});
                Toyun.share(shareInfo);
            }
        var picCallback = function(photos) {
            alert(photos);
        }
        var shareCallback = function(NSString){
            alert(NSString);
        }

            </script>
    </body>
</html>

test.html代码解释:

可能有些同窗对web前端的一些知识不太熟悉,稍微对这段代码作下解释,先说Toyun是iOS和Android这两边在本地要注入的一个对象【参考下面iOS的代码更容易明白】,充当原生应用和web页面之间的一个桥梁。页面上定义了两个按钮名字分别为点击我能够看到Xcode的NSLog点击我能够保持图片到手机相册。点击第一个按钮会经过Toyun这个桥梁调用本地应用的方法- (void)callCamera,没有传参;而点击第二个按钮会先调用本文件中的JavaScript方法callShare,这里将要保存的内容格式转成JSON字符串格式(这样作是为了适配Android,iOS能够直接接受JSON对象)而后再经过Toyun这个桥梁去调用原生应用的- (void)share:(NSString *)shareInfo方法这个是有传参的,参数为shareInfo。而下面的两个方法为原生方法调用后的回调方法,其中picCallback为获取图片成功的回调方法,而且传回拿到的参数photos;shareCallback为分享成功的回调方法。

IOS代码

经过JSContext,在ObjC中经过JSContext注入模型,而后调用模型的方法:

首先,咱们须要先定义一个协议,并且这个协议必需要遵照JSExport协议

@protocol JavaScriptObjectiveCDelegate <JSExport>
//协议方法
- (void)callCamera ;
- (void)share:(id)shareString ;
@end

接下来,咱们还须要定义一个模型,这个模型遵循咱们定义的协议

// 此模型用于注入JS的模型,这样就能够经过模型来调用方法.
@interface Model : NSObject<JavaScriptObjectiveCDelegate>

实现协议方法:

- (void)callCamera {
    
    NSLog(@"点击了网页按钮");
    
    JSValue *picCallback = self.jsContext[@"picCallback"];
    //参数传递
    [picCallback callWithArguments:@[@"请看Xcode!"]];
}

- (void)share:(id)shareString {
    
    NSLog(@"share:%@", shareString);
    
    NSData *json = [shareString dataUsingEncoding:NSUTF8StringEncoding];
    
    // 将json格式数据解析为对应的数据类型(数组、字典、字符串、数字)
    NSError *error = nil;
    // NSJSONReadingMutableContainers: 转为对应的可变容器(可变数组、可变字典)
    NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:json options:NSJSONReadingMutableContainers error:&error];
    
    NSLog(@"%@",jsonData[@"shareUrl"]);
    
    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:jsonData[@"shareUrl"]]];
        
    UIImage *image = [UIImage imageWithData:data];
    
    // 将图片保存到相册中
    
    UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL);
    
}

接下来,咱们在controller中在webview加载完成的代理中,给JS注入模型

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    // 经过模型调用方法,这种方式更好些。
    Model *model  = [[Model alloc] init];
    self.jsContext[@"Toyun"] = model;
    model.jsContext = self.jsContext;
    model.webView = self.webView;
    
    self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
        context.exception = exceptionValue;
        NSLog(@"异常信息:%@", exceptionValue);
    };
}

####4.2 拦截协议

拦截协议这个适合一些比较简单的一些状况,不须要引入什么框架,只须要web前端配合一下就好。可是在具体调用哪个方法上,以及在传值的时候可能会有些不方便,并且调用完后没法在回调JavaScript的方法。

test.html中的代码

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
</head>
<body>
    <div>
        <input type="button" value="CallCamera" onclick="callCamera()">
    </div>

<script>
    function callCamera() {
        window.location.href = 'toyun://callCamera';
    }
</script>
</body>
</html>

test.html中的代码解释:

这段代码相比上面的那段测试代码是很简单的,一样有一个按钮,名字为CallCamera点击以后调用本身的callCamera方法,window.location.href这里是改变主窗口的指向从而立刻发出一个连接为toyun://callCamera请求,而想要传给原生应用的参数也可已包含到此请求中,而在iOS方法中咱们要拦截这个请求,根据请求内容去判断JavaScript想要作的事情,从而实现web页面和本地应用之间的交互。

iOS对应的代码

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSString *url = request.URL.absoluteString;
    if ([url rangeOfString:@"toyun://"].location != NSNotFound) { 
        // url的协议头是toyun
        NSLog(@"callCamera");
        return NO;
    }
    return YES;
}

iOS对应的代码的解释:

在webView的代理方法中去拦截自定义的协议Toyun://若是是此协议则据此判断JavaScript想要作的事情,调用原生应用的方法,这些都是提早约定好的,同时阻止此连接的跳转。

Demo地址:

https://github.com/fuxinto/HfxDemo

相关文章
相关标签/搜索