iOS WKWebView——UIWebView的进阶

来源:互联网 发布:淘宝头条怎么申请 编辑:程序博客网 时间:2024/05/18 00:20

今天再次分享WKWebView。

是因为我又看到一篇比较好的博客,所以想记录下来。

什么是WKWebView,其作用与UIWebView基本一致。


那为什么明明有UIWebView苹果还要开发WKWebView呢?


嗯,少年(仙女),你很有想法。


技术需要与时俱进,

1.HTML5的大热必然对UIWebView的要求增高,

而WKWebView则具有较多的发言权,它能更多的支持HTML5的特性

2.官方宣称的高达60fps的滚动刷新率以及内置手势

3.将UIWebViewDelegate与UIWebView拆分成了14类与3个协议,以前很多不方便实现的功能得以实现

4.Safari相同的JavaScript引擎

5.占用更少的内存


文字表述并不是很清晰,我们需要直观的看到两者的区别。

UIWebView


UIWebView

WKWebView


WKWebView

因此,使用WkWebview替换UIWebView还是很有必要的。

基本使用方法


WKWebView有两个delegate,WKUIDelegateWKNavigationDelegate。WKNavigationDelegate主要处理一些跳转、加载处理操作,WKUIDelegate主要处理JS脚本,确认框,警告框等。因此WKNavigationDelegate更加常用。

比较常用的方法:

#pragma mark - lifeCircle- (void)viewDidLoad {    [super viewDidLoad];    webView = [[WKWebView alloc]init];    [self.view addSubview:webView];    [webView mas_makeConstraints:^(MASConstraintMaker *make) {        make.left.equalTo(self.view);        make.right.equalTo(self.view);        make.top.equalTo(self.view);        make.bottom.equalTo(self.view);    }];    webView.UIDelegate = self;    webView.navigationDelegate = self;    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];}#pragma mark - WKNavigationDelegate// 页面开始加载时调用- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{}// 当内容开始返回时调用- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{}// 页面加载完成之后调用- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{}// 页面加载失败时调用- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{}// 接收到服务器跳转请求之后调用- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{}// 在收到响应后,决定是否跳转- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{    NSLog(@"%@",navigationResponse.response.URL.absoluteString);    //允许跳转    decisionHandler(WKNavigationResponsePolicyAllow);    //不允许跳转    //decisionHandler(WKNavigationResponsePolicyCancel);}// 在发送请求之前,决定是否跳转- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{     NSLog(@"%@",navigationAction.request.URL.absoluteString);    //允许跳转    decisionHandler(WKNavigationActionPolicyAllow);    //不允许跳转    //decisionHandler(WKNavigationActionPolicyCancel);}#pragma mark - WKUIDelegate// 创建一个新的WebView- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{    return [[WKWebView alloc]init];}// 输入框- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler{    completionHandler(@"http");}// 确认框- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler{    completionHandler(YES);}// 警告框- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{    NSLog(@"%@",message);    completionHandler();}


OC与JS交互


WKWebview提供了API实现js交互 不需要借助JavaScriptCore或者webJavaScriptBridge。使用WKUserContentController实现js native交互。简单的说就是先注册约定好的方法,然后再调用。

JS调用OC方法

oc代码(有误,内存不释放):

@interface ViewController ()<WKUIDelegate,WKNavigationDelegate,WKScriptMessageHandler>{    WKWebView * webView;    WKUserContentController* userContentController;}@end@implementation ViewController#pragma mark - lifeCircle- (void)viewDidLoad {    [super viewDidLoad];    //配置环境    WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc]init];    userContentController =[[WKUserContentController alloc]init];    configuration.userContentController = userContentController;    webView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, 100, 100) configuration:configuration];    //注册方法    [userContentController addScriptMessageHandler:self  name:@"sayhello"];//注册一个name为sayhello的js方法    [self.view addSubview:webView];    [webView mas_makeConstraints:^(MASConstraintMaker *make) {        make.left.equalTo(self.view);        make.right.equalTo(self.view);        make.top.equalTo(self.view);        make.bottom.equalTo(self.view);    }];    webView.UIDelegate = self;    webView.navigationDelegate = self;    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.test.com"]]];}- (void)dealloc{    //这里需要注意,前面增加过的方法一定要remove掉。    [userContentController removeScriptMessageHandlerForName:@"sayhello"];}#pragma mark - WKScriptMessageHandler- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{    NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);}@end


上面的OC代码如果认证测试一下就会发现dealloc并不会执行,这样肯定是不行的,会造成内存泄漏。原因是[userContentController addScriptMessageHandler:self name:@"sayhello"];这句代码造成无法释放内存。(ps:试了下用weak指针还是不能释放,不知道是什么原因。)因此还需要进一步改进,正确的写法是用一个新的controller来处理,新的controller再绕用delegate绕回来。

oc代码(正确写法):

@interface ViewController ()<WKUIDelegate,WKNavigationDelegate,WKScriptMessageHandler>{    WKWebView * webView;    WKUserContentController* userContentController;}@end@implementation ViewController#pragma mark - lifeCircle- (void)viewDidLoad {    [super viewDidLoad];    //配置环境    WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc]init];    userContentController =[[WKUserContentController alloc]init];    configuration.userContentController = userContentController;    webView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, 100, 100) configuration:configuration];    //注册方法    WKDelegateController * delegateController = [[WKDelegateController alloc]init];    delegateController.delegate = self;    [userContentController addScriptMessageHandler:delegateController  name:@"sayhello"];    [self.view addSubview:webView];    [webView mas_makeConstraints:^(MASConstraintMaker *make) {        make.left.equalTo(self.view);        make.right.equalTo(self.view);        make.top.equalTo(self.view);        make.bottom.equalTo(self.view);    }];    webView.UIDelegate = self;    webView.navigationDelegate = self;    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.test.com"]]];}- (void)dealloc{    //这里需要注意,前面增加过的方法一定要remove掉。    [userContentController removeScriptMessageHandlerForName:@"sayhello"];}#pragma mark - WKScriptMessageHandler- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{    NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);}@end


WKDelegateController代码:

#import <UIKit/UIKit.h>#import <WebKit/WebKit.h>@protocol WKDelegate <NSObject>- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;@end@interface WKDelegateController : UIViewController <WKScriptMessageHandler>@property (weak , nonatomic) id<WKDelegate> delegate;@end


.m代码:

#import "WKDelegateController.h"@interface WKDelegateController ()@end@implementation WKDelegateController- (void)viewDidLoad {    [super viewDidLoad];}- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{    if ([self.delegate respondsToSelector:@selector(userContentController:didReceiveScriptMessage:)]) {        [self.delegate userContentController:userContentController didReceiveScriptMessage:message];    }}@end


h5代码:

<html><head>    <script>function say(){//前端需要用 window.webkit.messageHandlers.注册的方法名.postMessage({body:传输的数据} 来给native发送消息    window.webkit.messageHandlers.sayhello.postMessage({body: 'hello world!'});}</script></head>    <body>        <h1>hello world</h1>        <button onclick="say()">say hello</button>    </body></html>


打印出的log:

 
name:sayhello body:{    body = "hello world!";} frameInfo:<WKFrameInfo: 0x7f872060ce20; isMainFrame = YES; request = <NSMutableURLRequest: 0x618000010a30> { URL: http://www.test.com/ }>

注意点

  • addScriptMessageHandler要和removeScriptMessageHandlerForName配套出现,否则会造成内存泄漏。
  • h5只能传一个参数,如果需要多个参数就需要用字典或者json组装。

oc调用JS方法

代码如下:

- (void)webView:(WKWebView *)tmpWebView didFinishNavigation:(WKNavigation *)navigation{    //say()是JS方法名,completionHandler是异步回调block    [webView evaluateJavaScript:@"say()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {        NSLog(@"%@",result);    }];}


h5代码同上。

WebViewJavascriptBridge


一般来说,一个好的UI总有一个大神会开发出一个好的第三方封装框架。WebViewJavascriptBridge的作者也做了一套支持WKWebView与JS交互的第三方框架:WKWebViewJavascriptBridge。

  • cocoaPods: pod 'WebViewJavascriptBridge', '~> 5.0.5'

  • github地址:https://github.com/marcuswestin/WebViewJavascriptBridge

主要方法如下:

//初始化方法+ (instancetype)bridgeForWebView:(WKWebView*)webView;+ (void)enableLogging;//注册函数名- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler;//调用函数名- (void)callHandler:(NSString*)handlerName;- (void)callHandler:(NSString*)handlerName data:(id)data;- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback;//重置- (void)reset;//设置WKNavigationDelegate- (void)setWebViewDelegate:(id<WKNavigationDelegate>)webViewDelegate;


基本的实现方法和上面写的差不多,就是封装了一下,有兴趣的童鞋可以自己pod下来使用。


补充


前不久把项目中的UIWebView更新到WkWebView,解决了一大堆问题,但是也遗留了一大堆问题,比方说cookie。

以前UIWebView会自动去NSHTTPCookieStorage中读取cookie,但是WKWebView并不会去读取,因此导致cookie丢失以及一系列问题,解决方式就是在request中手动帮其添加上。

mainWebView.UIDelegate = self;mainWebView.navigationDelegate = self;NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.test.com"]];[request addValue:[self readCurrentCookieWithDomain:@"http://www.test.com/"] forHTTPHeaderField:@"Cookie"];[mainWebView loadRequest:request];- (NSString *)readCurrentCookieWithDomain:(NSString *)domainStr{    NSHTTPCookieStorage*cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];    NSMutableString * cookieString = [[NSMutableString alloc]init];    for (NSHTTPCookie*cookie in [cookieJar cookies]) {        [cookieString appendFormat:@"%@=%@;",cookie.name,cookie.value];    }//删除最后一个“;”    [cookieString deleteCharactersInRange:NSMakeRange(cookieString.length - 1, 1)];    return cookieString;}


但是这只能解决第一次进入的cookie问题,如果页面内跳转(a标签等)还是取不到cookie,因此还要再加代码。

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {   //取出cookie    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];    //js函数    NSString *JSFuncString =@"function setCookie(name,value,expires)\    {\    var oDate=new Date();\    oDate.setDate(oDate.getDate()+expires);\document.cookie=name+'='+value+';expires='+oDate+';path=/'\    }\    function getCookie(name)\    {\    var arr = document.cookie.match(new RegExp('(^| )'+name+'=({FNXX==XXFN}*)(;|$)'));\    if(arr != null) return unescape(arr[2]); return null;\    }\    function delCookie(name)\    {\    var exp = new Date();\    exp.setTime(exp.getTime() - 1);\    var cval=getCookie(name);\    if(cval!=null) document.cookie= name + '='+cval+';expires='+exp.toGMTString();\    }";    //拼凑js字符串    NSMutableString *JSCookieString = JSFuncString.mutableCopy;    for (NSHTTPCookie *cookie in cookieStorage.cookies) {    NSString *excuteJSString = [NSString stringWithFormat:@"setCookie('%@', '%@', 1);", cookie.name, cookie.value];        [JSCookieString appendString:excuteJSString];    }    //执行js    [webView evaluateJavaScript:JSCookieString completionHandler:nil];}





原文链接:http://www.jianshu.com/p/4fa8c4eb1316
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 顺丰快递暴力运输产品坏了怎么办 亚航订机票名字少写一个字母怎么办 如果淘宝付款七天内不发货怎么办 浪琴手表调了一下日期不走了怎么办 收件人号码写错快递柜已签收怎么办 医院名字写错了怎么办保险报销 电脑在使用中出现了英文字慕怎么办 下雨天了怎么办我好想你是什么歌 安卓手机不支持微信运动怎么办 装系统时无法跳过密匙怎么办 在msdn里下的系统没有网怎么办 w7电脑更新后系统没法激活怎么办 手机使用加速器后网速变卡怎么办 奥特曼ol分解了迪迦石像怎么办 左右棋牌游戏兑换总说系统护怎么办 四季海棠扦插以后黄叶卷叶怎么办 竹节海棠浇水多了叶子蔫了怎么办 社保停缴了里面的钱怎么办 王者荣耀英雄释放技能有延迟怎么办 买的桑拿木板颜色太深了怎么办 万一填写了奔跑吧诈骗信息该怎么办 深圳限行时段堵在路上怎么办 开车堵在路上到了限行时间怎么办 兄妹之间都不想照顾母亲我该怎么办 小孩扁体发炎睡觉呼吸声沉重怎么办 客所思pk3老驱动有杂音怎么办 手机打不开解压包密码怎么办 在香港专柜买东西柜员少给货怎么办 恶魔猎手第二神器任务没选择怎么办 电脑放久了开不了机怎么办 你在主持时说错话了怎么办 1、你在主持时说错话了怎么办? 领导让你替他参加重要会议怎么办 在备孕期老公照了片怎么办 和混混打架后被混混纠缠怎么办 开了一年的瑜伽馆想关掉会员怎么办 广东工厂宿舍里面床板有臭虫怎么办 胃癌手术后12天引流液多怎么办 八个月了胎儿头还在上面怎么办 喂奶一个月后奶头又裂开了怎么办 孩子吃奶吃的奶头裂开了怎么办