apple官方文档https://developer.apple.com/documentation/webkit
WKWebView 是苹果在iOS 8中引入的新组件,目的是提供一个现代的支持最新Webkit功能的网页浏览控件,摆脱过去 UIWebView的老、旧、笨,特别是内存占用量巨大的问题。它使用与Safari中一样的Nitro JavaScript引擎,大大提高了页面js执行速度。
相比于UIWebView的优势:
在性能、稳定性、占用内存方面有很大提升;
允许JavaScript的Nitro库加载并使用(UIWebView中限制)
增加加载进度属性:estimatedProgress,不用在自己写假进度条了 支持了更多的HTML的属性
具体分析WKWebView的优劣势
内存占用是UIWebView的1/4~1/3
页面加载速度有提升,有的文章说它的加载速度比UIWebView提升了一倍左右。
更为细致地拆分了 UIWebViewDelegate 中的方法
自带进度条。不需要像UIWebView一样自己做假进度条(通过NJKWebViewProgress和双层代理技术实现),技术复杂度和代码量,根贴近实际加载进度优化好的多。
允许JavaScript的Nitro库加载并使用(UIWebView中限制)
可以和js直接互调函数,不像UIWebView需要第三方库WebViewJavascriptBridge来协助处理和js的交互。
不支持页面缓存,需要自己注入cookie,而UIWebView是自动注入cookie。
无法发送POST参数问题
基本参数解释:
WKWebView:网页的渲染与展示,通过WKWebViewConfiguration可以进行自定义配置。
WKWebViewConfiguration:这个类专门用来配置WKWebView。
WKPreference:这个类用来进行相关webView设置。
WKProcessPool:这个类用来配置进程池,与网页视图的资源共享有关。
WKUserContentController:这个类主要用来做native与JavaScript的交互管理。
WKUserScript:用于进行JavaScript注入。
WKScriptMessageHandler:这个类专门用来处理JavaScript调用native的方法。
WKNavigationDelegate:网页跳转间的导航管理协议,这个协议可以监听网页的活动。
WKNavigationAction:网页某个活动的示例化对象。
WKUIDelegate:用于交互处理JavaScript中的一些弹出框。
WKBackForwardList:堆栈管理的网页列表。
WKBackForwardListItem:每个网页节点对象。
1.加载网页
WKWebViewConfiguration *webConfiguration = [WKWebViewConfiguration new];
WKWebView *webView=[[WKWebView alloc]initWithFrame:CGRectZero configuration:webConfiguration];
[self.view addSubview:self.webView];
[webView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.offset(self.zxNavigationbarHeight);
make.left.right.offset(0);
make.bottom.offset(0);
}];
NSURL *url = [NSURL URLWithString:self.strUrl];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[webView loadRequest:request];
2.进度条和标题
_progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, self.zxNavigationbarHeight, self.view.frame.size.width, 2)];
_progressView.tintColor = [UIColor blueColor];
_progressView.trackTintColor = [UIColor clearColor];
[self.view addSubview:_progressView];
//添加监测网页加载进度的观察者
[self.webView addObserver:self
forKeyPath:NSStringFromSelector(@selector(estimatedProgress))
options:0
context:nil];
[self.webView addObserver:self
forKeyPath:@"title"
options:NSKeyValueObservingOptionNew
context:nil];
//kvo 监听进度和标题 必须实现此方法
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context{
if ([keyPath isEqualToString:NSStringFromSelector(@selector(estimatedProgress))]
&& object == _webView) {
DDLog(@"网页加载进度 = %f",_webView.estimatedProgress);
self.progressView.progress = _webView.estimatedProgress;
if (_webView.estimatedProgress >= 1.0f) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.progressView.progress = 0;
});
}
}else if([keyPath isEqualToString:@"title"]
&& object == _webView){
self.navigationItem.title = _webView.title;
}else{
[super observeValueForKeyPath:keyPath
ofObject:object
change:change
context:context];
}
}
- (void)dealloc{
//移除观察者
[_webView removeObserver:self
forKeyPath:NSStringFromSelector(@selector(estimatedProgress))];
[_webView removeObserver:self
forKeyPath:NSStringFromSelector(@selector(title))];
}
3.刷新、上一页、下一页
- (void)goBackAction:(id)sender{
if ([self.webView canGoBack]) {
[self.webView goBack];
}
}
- (void)refreshAction:(id)sender{
[self.webView reload];
}
-(void)goNextAction:(id)sender{
if ([self.webView canGoForward]) {
[self.webView goForward];
}
}
4.OC调用JS
//OC调用JS
- (void)ocToJs{
//changeColor()是JS方法名,completionHandler是异步回调block
NSString *jsString = [NSString stringWithFormat:@"changeColor('%@')", @"Js颜色参数"];
[_webView evaluateJavaScript:jsString completionHandler:^(id _Nullable data, NSError * _Nullable error) {
DDLog(@"改变HTML的背景色");
}];
//改变字体大小 调用原生JS方法
NSString *jsFont = [NSString stringWithFormat:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '%d%%'", arc4random()%99 + 100];
[_webView evaluateJavaScript:jsFont completionHandler:nil];
NSString * path = [[NSBundle mainBundle] pathForResource:@"girl" ofType:@"png"];
NSString *jsPicture = [NSString stringWithFormat:@"changePicture('%@','%@')", @"pictureId",path];
[_webView evaluateJavaScript:jsPicture completionHandler:^(id _Nullable data, NSError * _Nullable error) {
DDLog(@"切换本地头像");
}];
}
//OC调用JS改变背景色
function changeColor(parameter)
{
document.body.style.backgroundColor = randomColor();
}
5.JS调用OC
//方式一.WKNavigationDelegate通过即将跳转的url拦截
// 根据WebView对于即将跳转的HTTP请求头信息和相关信息来决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSString * urlStr = navigationAction.request.URL.absoluteString;
NSLog(@"发送跳转请求:%@",urlStr);
//自己定义的协议头
NSString *htmlHeadString = @"github://";
if([urlStr hasPrefix:htmlHeadString]){
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"通过截取URL调用OC" message:@"你想前往我的Github主页?" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
}])];
[alertController addAction:([UIAlertAction actionWithTitle:@"打开" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSURL * url = [NSURL URLWithString:[urlStr stringByReplacingOccurrencesOfString:@"github://callName_?" withString:@""]];
[[UIApplication sharedApplication] openURL:url];
}])];
[self presentViewController:alertController animated:YES completion:nil];
decisionHandler(WKNavigationActionPolicyCancel);
}else{
decisionHandler(WKNavigationActionPolicyAllow);
}
}
//方式二.WKScriptMessageHandler注册相应交互事件
//自定义的WKScriptMessageHandler 是为了解决内存不释放的问题
WeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];
//这个类主要用来做native与JavaScript的交互管理
WKUserContentController * wkUController = [[WKUserContentController alloc] init];
//注册一个name为jsToOcNoPrams的js方法 设置处理接收JS方法的对象
[wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"jsToOcNoPrams"];
[wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"jsToOcWithPrams"];
config.userContentController = wkUController;
- (void)dealloc{
//移除注册的js方法
[[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsToOcNoPrams"];
[[_webView configuration].userContentController removeScriptMessageHandlerForName:@"jsToOcWithPrams"];
}
//被自定义的WKScriptMessageHandler在回调方法里通过代理回调回来,绕了一圈就是为了解决内存不释放的问题
//通过接收JS传出消息的name进行捕捉的回调方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
DDLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);
//用message.body获得JS传出的参数体
NSDictionary * parameter = message.body;
//JS调用OC
if([message.name isEqualToString:@"jsToOcNoPrams"]){
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"js调用到了oc" message:@"不带参数" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
}])];
[self presentViewController:alertController animated:YES completion:nil];
}else if([message.name isEqualToString:@"jsToOcWithPrams"]){
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"js调用到了oc" message:parameter[@"params"] preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
}
function jsToOcFunction1()
{
window.webkit.messageHandlers.jsToOcNoPrams.postMessage({});
}
function jsToOcFunction2()
{
window.webkit.messageHandlers.jsToOcWithPrams.postMessage({"params":"我是参数"});
}
//方式三.WKUIDelegate捕获系统交互
/**
* web界面中有弹出警告框时调用
*
* @param webView 实现该代理的webview
* @param message 警告框中的内容
* @param completionHandler 警告框消失调用
*/
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"HTML的弹出框" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
// 确认框
//JavaScript调用confirm方法后回调的方法 confirm是js中的确定框,需要在block中把用户选择的情况传递进去
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler(NO);
}])];
[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(YES);
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
// 输入框
//JavaScript调用prompt方法后回调的方法 prompt是js中的输入框 需要在block中把用户输入的信息传入
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.text = defaultText;
}];
[alertController addAction:([UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(alertController.textFields[0].text?:@"");
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
// 页面是弹出窗口 _blank 处理
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
return nil;
}
function showAlert()
{
alert("被OC截获到了");
}