UIWebView的ios和js的交互(一)

githubDemo:
https://github.com/wangjinshan/JSWebDemo

项目展示

![F106F120-3B49-410F-8656-16AE08F8160A.png](http://upload-images.jianshu.io/upload_images/2845360-34023a8a8e63fe1c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
9EE9D7D7-ADA9-4827-A6D4-F2908D379622.png

![7D18E6EB-A9BF-4CF4-9E97-71B8E4E32CF9.png](http://upload-images.jianshu.io/upload_images/2845360-b4173b6d118a0e15.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

E52E3FEB-D13F-4F44-818A-044287CB56A9.png

认识UIWebView

UIWebView继承自uiview 是用来加载网页的类,可以简单理解成就是一个view
完整的UIWebView创建
1加载网络数据

self.webView = [[UIWebView alloc] initWithFrame:self.view.frame];
    NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://lianghui.huanqiu.com/2017/roll/2017-03/10304840.html"]];
    [self.view addSubview: _webView];
    [_webView loadRequest:request];

2, 加载本地的数据

    NSString *path =[[NSBundle mainBundle]pathForResource:@"JSShareSDKDemo" ofType:@"html"];
    NSURL *url =[NSURL URLWithString:path];
    NSURLRequest *request =[NSURLRequest requestWithURL:url];
    self.webView =[[UIWebView alloc]initWithFrame:self.view.frame];
    [self.webView loadRequest:request];
    [self.view addSubview:self.webView];

加载

加载的方式给了以下三种

/**通过NSURLRequest去加载html界面**/
- (void)loadRequest:(NSURLRequest *)request;
/**加载html格式的字符串,其中的baseUrl下面会介绍**/
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
/**这种方式表示没见到过,我也不知道是什么,有兴趣的可以自己去查查**/
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL;(*很少用*)

-(void)loadRequest:(NSURLRequest *)request 方法即可以去通过网络连接加载html资源,也可以去加载本地的html资源

   //    加载网络html
    NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://shenzoom.com"]];
    [self.myWebview loadRequest:request];
  • (void) loadHTMLString:(NSString )string baseURL:(nullable NSURL )baseURL方法一般用来加载本地的html界面
-(void)loadHtmlWithString
{
//    加载css
    NSURL *cssPath = [[NSBundle mainBundle]URLForResource:@"ShareSDK" withExtension:@"css"];
//    创建css标签
    NSString *css = [NSString stringWithFormat:@"<link href =\"%@\" rel = %@>",cssPath,@"\"stylesheet\""];
    NSLog(@"--css--%@",css);
    NSString *html =[NSString stringWithFormat:@"<html><head>%@</head><body><p>网页中的文字</p> <button id=\"login\" onclick=\"login()\">点击按钮</button> <img src=\"%@\" alt="">   </body></html>",css,css];
    [self.myWebview loadHTMLString:html baseURL:nil];
}

注意:baseURL用来确定htmlString的基准地址,相当于HTML的<base>标签的作用,定义页面中所有链接的默认地址。具体查看W3C上的base标签。baseURL是HTML字符串中引用到资源的查找路径,当HTML中没有引用外部资源时,可以指定为nil;若引用了外部资源(外部资源:除了html代码以外,界面中所有的图片,链接都属于外部资源),一般情况下使用mainBundle的路径即可。在实际操作中,常常会出现「文本显示正常,图片无法显示」等情况,若HTML文本中引用外部资源都是使用相对路径,则出现这种问题的原因一般都是baseURL参数错误

属性

//    **webView的代理**
    @property (nullable, nonatomic, assign) id <UIWebViewDelegate> delegate;
//    **内置的scrollView**
    @property (nonatomic, readonly, strong) UIScrollView
//    *scrollView NS_AVAILABLE_IOS(5_0);
//    **URL请求**
    @property (nullable, nonatomic, readonly, strong) NSURLRequest *request;
//    **是否缩放到适合屏幕大小**
    @property (nonatomic) BOOL scalesPageToFit;
//    **执行javaScript操作**
    - (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
加载属性
- (void)reload; //重新加载数据
- (void)stopLoading; //停止加载数据
@property (nonatomic, readonly, getter=isLoading) BOOL loading; //是否正在加载
- (void)goBack; //返回上一级
- (void)goForward; //跳转下一级
@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack; //能否返回上一级
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward; //能否跳转下一级
多媒体属性
//YES,自动检测网页上的电话号码,单击可以拨打,_ 已经废弃_
@property (nonatomic) BOOL detectsPhoneNumbers NS_DEPRECATED_IOS(2_0, 3_0);
//设置某些数据变为链接形式,这个枚举可以设置如电话号,地址,邮箱等转化为链接
@property (nonatomic) UIDataDetectorTypes dataDetectorTypes NS_AVAILABLE_IOS(3_0);
//设置是否使用内联播放器播放视频
@property (nonatomic) BOOL allowsInlineMediaPlayback NS_AVAILABLE_IOS(4_0); // defaults to YES
//设置视频是否自动播放
@property (nonatomic) BOOL mediaPlaybackRequiresUserAction NS_AVAILABLE_IOS(4_0); // default to YES
//设置音频播放是否支持ari play功能
@property (nonatomic) BOOL mediaPlaybackAllowsAirPlay NS_AVAILABLE_IOS(5_0); //  default to YES
//设置是否将数据加载如内存后渲染界面
@property (nonatomic) BOOL suppressesIncrementalRendering NS_AVAILABLE_IOS(6_0); //  default to NO
//设置用户交互模式
@property (nonatomic) BOOL keyboardDisplayRequiresUserAction NS_AVAILABLE_IOS(6_0);

iOS7.0 新特性
@property (nonatomic) UIWebPaginationMode paginationMode NS_AVAILABLE_IOS(7_0);
这个属性用来设置一种模式,当网页的大小超出view时,将网页以翻页的效果展示,枚举如下:
typedef NS_ENUM(NSInteger, UIWebPaginationMode) 
{ 
  UIWebPaginationModeUnpaginated, //不使用翻页效果 
  UIWebPaginationModeLeftToRight, //将网页超出部分分页,从左向右进行翻页 
  UIWebPaginationModeTopToBottom, //将网页超出部分分页,从上向下进行翻页 
  UIWebPaginationModeBottomToTop, //将网页超出部分分页,从下向上进行翻页 
  UIWebPaginationModeRightToLeft //将网页超出部分分页,从右向左进行翻页
} __TVOS_PROHIBITED;
//设置每一页的长度
@property (nonatomic) CGFloat pageLength NS_AVAILABLE_IOS(7_0);
//设置每一页的间距
@property (nonatomic) CGFloat gapBetweenPages NS_AVAILABLE_IOS(7_0);
//获取分页数
@property (nonatomic, readonly) NSUInteger pageCount NS_AVAILABLE_IOS(7_0);
// 未知
@property (nonatomic) UIWebPaginationBreakingMode paginationBreakingMode NS_AVAILABLE_IOS(7_0);
typedef NS_ENUM(NSInteger, UIWebPaginationBreakingMode) 
{ 
  UIWebPaginationBreakingModePage,       
  UIWebPaginationBreakingModeColumn
} __TVOS_PROHIBITED;
iOS9.0新特性
//是否允许画中画播放/调用系统的视频播放器
@property (nonatomic) BOOL allowsPictureInPictureMediaPlayback NS_AVAILABLE_IOS(9_0);
@property (nonatomic) BOOL allowsLinkPreview NS_AVAILABLE_IOS(9_0); //
UIWebView的代理方法 UIWebViewDelegate
self.myWebview.delegate =self;
//代理方法
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    /**返回YES,进行加载。通过UIWebViewNavigationType可以得到请求发起的原因
     如果为webView添加了delegate对象并实现该接口,那么在webView加载任何一个frame之前都会delegate对象的该方法,该方法的返回值用以控制是否允许加载目标链接页面的内容,返回YES将直接加载内容,NO则反之。并且UIWebViewNavigationType枚举,定义了页面中用户行为的分类,包括;
     UIWebViewNavigationTypeLinkClicked,0 用户触击了一个链接。
     UIWebViewNavigationTypeFormSubmitted,1 用户提交了一个表单。
     UIWebViewNavigationTypeBackForward,2, 用户触击前进或返回按钮。
     UIWebViewNavigationTypeReload,3, 用户触击重新加载的按钮。
     UIWebViewNavigationTypeFormResubmitted,4,用户重复提交表单
     UIWebViewNavigationTypeOther,5, 发生其它行为。
     */
//    NSLog(@"----request-------%@",request);
    NSLog(@"----navigationType-------%ld",(long)navigationType);
    return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
    //开始加载,可以加上风火轮(也叫菊花)
}

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    //完成加载
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    //加载出错
}
禁用页面滚动弹跳
self.myWebview.scrollView.bounces = false;  //禁用页面滚动弹跳

基于UIWebView ios与js交互

ios 原声界面上加载webview, 然后需要和web界面进行数据交互就需要用到 ios和js 交互的知识, 交互的方法有三种:
1, 今天要讲解的基于 UIWebView的 交互,这种方式最主要的思想就是通过网页的代码方法进行url的拦截操作,后面细讲
2 WKWebView 的第三方库
3, 苹果为我们封装 JavaScriptCore.framework

实例讲解

1,配置基本环境
创建 Sample.html ShareSDK.css ShareSDK.js 实现界面布局并加载到本地

  代理  UIWebViewDelegate
  NSString *path = [[NSBundle mainBundle]pathForResource:@"Sample" ofType:@"html"];
    NSURL*htmlURL = [NSURL fileURLWithPath:path];
    self.webView =[[UIWebView alloc]initWithFrame:self.view.bounds];
    NSURLRequest *request =[NSURLRequest requestWithURL:htmlURL];
    [_webView loadRequest:request];
    _webView.delegate = self;
    [self.view addSubview:_webView];

以下以在html上集成集成 ShareSDK 为例子, 由于前期数据传输没整理好,大家先忽略数据传递的问题,文章后面会补充
1, ShareSDK 初始化 ,创建一个 名字叫ShareSDK的对象 并在里面实现 initSDK初始化方法 ,数据暂时将以数组的形式传递,后面再补充上对象json传递

function ShareSDK()
{
//    初始化sdk, 注意 此处必须写上this 否则外部调用失败
    this.initSDK = function()
    {
        //1,平台的参数
        var mobkey = 'iosv1101';
        var platformID = new PlatformID();
        //平台数组
        var platformArr = [platformID.platformID.WeChat,platformID.platformID.WeChatFavorites,platformID.platformID.WeChatMoments];
        //2,微信appkey
        var platformConfig = ["wx4868b35061f87885","64020361b8ec4c99936c0e3999a9f249"];
        //发送请求
        window.location.href = '&initSDK' + '&mobkey'+mobkey +'&platformArr'+ platformArr +'&platformConfig' + platformConfig;
    }
}
var $sharesdk = new ShareSDK();

大家先简单理解这个就是一个 js方法,下面我们要做的就是在 ios上调用这段代码
我们已经在 viewDidLoad 的方法中加载了网页 下面我们就签UIWebView协议 UIWebViewDelegate 并实现代理方法

ios 调用 js代码

ios 调用js 代码非常简单,UIWebView 已经为我们封装好了方法(stringByEvaluatingJavaScriptFromString),我们将在网页加载完毕后进行代码处理,注意必须在网页加载完毕才能操作,否则无效

// 网页完成加载
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
//  注意stringByEvaluatingJavaScriptFromString必须保证是在主线线程中完成任务
    [self.webView stringByEvaluatingJavaScriptFromString:@"window.$sharesdk.initSDK()"];
    NSLog(@"----1------%@",[NSThread currentThread]);   //线程是1
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_sync(queue, ^{
        NSLog(@"----2-----%@",[NSThread currentThread]); //线程还是1
         [self.webView stringByEvaluatingJavaScriptFromString:@"window.$sharesdk.ajsTest()"];
    });
}

注意 (stringByEvaluatingJavaScriptFromString) 这个方法必须在主线程调用,否则崩溃,如下

Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...

代码注释: ios 调用js 相当于 给 js 发送一段字符串,然后程序会根据字符串去环境中寻找对应的方法并执行, @"window.$sharesdk.initSDK()" window js是环境中的全局变量, 所有 js环境中方法和属性 ,都可以通过 window 来进行调用, 此处 window 去找到 sharesdk对象并执行 initSDK()的方法
检查 方法有没有被调用 可以在 initSDK(); 进行 alert();进行测试,弹出警告窗口则表示调用成功

js 调用 ios代码

js调用 ios 也非常的简单,就是js发送一段请求,然后ios 在代理方法中进行拦截,如下

 this.initSDK = function()
    {
        //发送请求
        window.location.href = '要发送的数据';
    }

js 中 只要遇到 window.location.href 就会通知 ios 执行回调方法, html 进行 某个时段进行固定跳转也是经过这个方法, 下面我们就可以到 ios 对这段数据进行解析

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    [self analyzeURL:request.URL];
    return YES;   // 必须返回 yes
}

以上方法中的 request 就携带了 window.location.href = '要发送的数据'; 这个方法中 携带的 [要发送的数据], 这是一段 json 数据 ,解析这段json数据 你就可以进行相关的操作了, 到此 ios调用 js is调用ios结束,感觉非常简单, 但是你发现没有, 以上的实现并没有牵扯到数据, 个人认为 交互最蛋疼一点就是数据传递, UIWebView 并不友好, 数据传递比较蛋疼

js传递数据给ios

上面说过 js 调用 ios 是通过 发送请求的方法 (window.location.href = '要发送的数据';) 但是这个数据到底应该怎么写? 如下

//    测试传递对象类型
    this.ajsTest = function ()
    {
        var backJson = {};
        var wjs =
        {
            "name":"神族科技CEO",
            "name":"金山",
            "city":"上海"
        };
        backJson["wjs"] = wjs;
        var wjsJson =  ObjectToJsonString(backJson);
        window.location.href ="ajstest://?"+wjsJson;    // 注意协议头必须是小写 大写将转换成小写
    }

通常为了解析方便,我们都会携带一个协议,这个协议头可以随便写,然后拼接你要的数据,上面的例子 传递的是 一个js 中的对象,我们需要将 对象转换成 js 格式的字符串, 方法就是

var JsonStringToObject = function (string)
{
    try
    {
        return eval("(" + string + ")");
    }
    catch (err)
    {
        return null;
    }
};

这样就可以把js中的对象数据传给ios使用,ios拦截到的依然是个 json的字符串需要你手动解析

ios传递数据给js

ios 传递数据给 js 实现和ios 调用js 代码一样,只需要将传递的数据拼接在后面就可以

[errorDic setObject:[NSDictionary dictionaryWithObjectsAndKeys:
                                         [NSNumber numberWithInteger:[error code]],
                                         @"error_code",
                                         [error userInfo],
                                         @"error_msg",
                                         nil]
                                 forKey:@"error"];
                    [self.webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"$sharesdk.callBackData('%@')",[self jsonStringFromObject:errorDic]]];
/**
 *  对象序列化为Json字符串
 *
 *  @param object 任意对象
 *
 *  @return Json字符串
 */
- (NSString *)jsonStringFromObject:(id)object
{
    NSString *jsonString = [[NSString alloc]init];
    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:object
                                                      options:NSJSONWritingPrettyPrinted
                                                        error:&error];
    if (! jsonData) {
        NSLog(@"error: %@", error);
    } else {
        jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    }
    NSMutableString *mutStr = [NSMutableString stringWithString:jsonString];
    NSRange range = {0,jsonString.length};
    [mutStr replaceOccurrencesOfString:@" "withString:@""options:NSLiteralSearch range:range];
    NSRange range2 = {0,mutStr.length};
    [mutStr replaceOccurrencesOfString:@"\n"withString:@""options:NSLiteralSearch range:range2];
    return mutStr;
}

这样就是实现了 ios 把一个 ios对象 转换成json 对象并 传给 js 使用
到此 基于 UIWebView 的 ios与js交互全部结束
完美的分割线


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,997评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,603评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,359评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,309评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,346评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,258评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,122评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,970评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,403评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,596评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,769评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,464评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,075评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,705评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,848评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,831评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,678评论 2 354

推荐阅读更多精彩内容