iOS之WKWebView
Xcode8发布以后,编译器开始不支持IOS7,所以很多应用在适配IOS10之后都不在适配IOS7了,其中包括了很多大公司,网易新闻,滴滴出行等。因此,我们公司的应用也打算淘汰IOS7。
支持到IOS8,第一个要改的自然是用WKWebView替换原来的UIWebView。WKWebView有很多明显优势:
更多的支持HTML5的特性
官方宣称的高达60fps的滚动刷新率以及内置手势
将UIWebViewDelegate与UIWebView拆分成了14类与3个协议,以前很多不方便实现的功能得以实现。文档
Safari相同的JavaScript引擎
占用更少的内存
UIWebView

WKWebView

因此,使用WkWebview替换UIWebView还是很有必要的。
基本使用方法
WKWebView有两个delegate,WKUIDelegate 和 WKNavigationDelegate。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 (){
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 (){
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 #import @protocol WKDelegate - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message; @end @interface WKDelegateController : UIViewController @property (weak , nonatomic) id 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代码:
function say()
{
//前端需要用 window.webkit.messageHandlers.注册的方法名.postMessage({body:传输的数据} 来给native发送消息
window.webkit.messageHandlers.sayhello.postMessage({body: 'hello world!'});
} hello world say hello
打印出的log:
name:sayhello
body:{
body = "hello world!";
}
frameInfo:<wkframeinfo: 0x7f872060ce20; ismainframe = yes; request = { 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)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];
}
iOS之WKWebView的更多相关文章
- iOS 8 WKWebView
首先看看这篇文章,写得很好:http://nshipster.cn/wkwebkit/ 再推荐去看看 iOS_8_by_Tutorials 这本书里的 WKWebView相关章节! 我这里说下自己的简 ...
- iOS 8 WKWebView 知识点
首先看看这篇文章,写得很好:http://nshipster.cn/wkwebkit/ 再推荐去看看 iOS_8_by_Tutorials 这本书里的 WKWebView相关章节! 我这里说下自己的简 ...
- iOS(WKWebView)OC与JS交互 之三
随着H5功能愈发的强大,没进行过混合开发的小伙们都不好意思说自己能够独立进行iOS的app开发,在iOS7操作系统下,常用的native,js交互框架有easy-js,WebViewJavascr ...
- iOS 添加WKWebView导致控制器无法释放的问题
在WkWebView与JavaScript交互中,经常会在原生中注入MessageHandler,app中注入MessageHandler的方法 WKWebViewConfiguration *con ...
- iOS - 让WKWebView 支持 NSURLProtocol
iOS8以后,苹果推出了新框架Webkit,提供了替换UIWebView的组件WKWebView.各种UIWebView的问题没有了,速度更快了,占用内存少了,一句话,WKWebView是App内部加 ...
- iOS之WKWebView加载的网页自适应大小
一,前言 有时候在WKWebView加载页面后会发现页面的字会很小, 这是因为原网页没有做手机屏幕尺寸的适配, 那么在后台不做调整的情况下我们移动端怎样来适配页面呢? 以下代码可以适配大小(原本不可以 ...
- iOS - 使用WKWebView时OC调JS的user-select属性控制用户操作
// 页面加载完成之后调用 - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigatio ...
- iOS:WKWebView(19-01-31更)
以前用得不多,先开一篇,以后有遇到再补充. 1.返回 2.JS 调用 OC 3.获取.修改.添加网页信息 1.返回 if (self.mWebView.canGoBack == YES) { [sel ...
- ios 解决Wkwebview闪烁问题
// 网页闪烁问题 if ([self.webView.realWebView isKindOfClass:[WKWebView class]]) { ((WKWebView * ...
- iOS中wkwebview加载本地html的要点
项目中有些页面,我采用了html页面开发,然后用wkwebview加载的设计.在加载过程中遇见了一些问题,在这里进行一些记载和讨论.如有不同意见欢迎进行评论沟通. 问题时候这样的: 在webview的 ...
随机推荐
- Codevs 3409 搬运礼物
3409 搬运礼物 CodeVS原创 时间限制: 1 s 空间限制: 64000 KB 题目等级 : 青铜 Bronze 题解 题目描述 Description 小浣熊松松特别喜欢交 ...
- 洛谷P3338 [ZJOI2014]力(FFT)
传送门 题目要求$$E_i=\frac{F_i}{q_i}=\sum_{j=1}^{i-1}\frac{q_j}{(i-j)^2}-\sum_{j=i+1}^n\frac{q_j}{(j-i)^2}$ ...
- Java 工程师面试题和笔试题整理(一)
根据自己之前收集的还有一部分自己面试的整理出来,希望能帮到面试的兄弟(2017). 海科融通 笔试题 1.有一个字符串,如果要在其中查找一个子串,都有哪些方式,写出你认为最好的一个. 2.写出线程都有 ...
- 一个Java语言所写的shop网站框架明细
核心框架Spring Framework :作为一个优秀的开源框架,是为了解决企业应用程序开发复杂性而创建的.框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用 ...
- CVE-2017-5638——S2-045
一. 漏洞简介 Apache Struts是美国阿帕奇(Apache)软件基金会负责维护的一个开源项目,是一套用于创建企业级Java Web 应用的开源MVC框架,主要提供两个版本框架产品: Stru ...
- struts2的@Result annotation 如何添加params,并且在页面取值
http://www.bubuko.com/infodetail-2492575.html .............................................. 标签:lai ...
- css未知宽度水平居中整理
1.text-align 兼容性很好 .wp {text-align: center;} .test {display: inline;} <ul class="wp"> ...
- 安卓新的联网方式 Volley的使用(2)
如果使用volley 获取 网络图片 最好还是用 Volley 提供的NetworkImageView类, 可以很轻松的 完成工作, 而且他可以设置缓存, lru 和 sd卡的缓存.一些都封装好了. ...
- Linux磁盘根目录满了问题解析
linux里的log文件被删除后,空间没有被释放,是因为在Linux系统中,通过rm或者文件管理器删除文件将会从文件系统的目录结构上解除链接(unlink).然而如果文件是被打开的(有一个进程正在使用 ...
- 开源GIS-对比GeoServer和MapServer
1 主流组合: 基于C.C++系列的:Mapserver(服务器)+QGIS(桌面软件)+PostGIS(数据库)+Openlayers(JS)/ openscale (FLex)(浏览器客户端) ...