前言
Xcode8发布之后,编译器开始不支持 iOS7了.很多应用在适配 iOS10之后都不再适配 iOS7了.最低适配到 iOS8,所以第一个要更改的是用 WKWebView
替换原来的UIWebView
.并且 WKWebView 有很多明显的优势.
- 更多的支持 HTML5的特性
- 官方宣称的高达60fps 的滚动刷新率以及内置手势
- 将 UIWebViewDelegate 与 UIWebView 拆分了14个类和3个协议,以前很多不方便实现的功能得以实现.文档
- 和Safair 相同的 JavaScript 引擎
- 占用更少的内存
一 基本使用方法
WKWebView有两个delegate,WKUIDelegate 和 WKNavigationDelegate。WKNavigationDelegate主要处理一些跳转、加载处理操作,WKUIDelegate主要处理JS脚本,确认框,警告框等。因此WKNavigationDelegate更加常用。
- 初始化 WKWebView
- (void)drawUI {
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
_webView = webView;
webView.UIDelegate = self;
webView.navigationDelegate = self;
[self.view addSubview:webView];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];
}
- WKNavigationDelegate代理方法
// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@"didStartProvisionalNavigation");
}
// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
NSLog(@"didCommitNavigation");
}
// 页面加载完成时调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
NSLog(@"didFinishNavigation");
//say()是JS方法名,completionHandler是异步回调block
[webView evaluateJavaScript:@"say()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"evaluateJavaScript %@",result);
}];
}
// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
NSLog(@"didFailProvisionalNavigation");
}
// 接收到服务器跳转请求之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
}
// 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
NSLog(@"decidePolicyForNavigationResponse:%@",navigationResponse.response.URL.absoluteString);
//允许跳转
decisionHandler(WKNavigationResponsePolicyAllow);
//不允许跳转
//decisionHandler(WKNavigationResponsePolicyCancel);
}
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSLog(@"decidePolicyForNavigationAction:%@",navigationAction.request.URL.absoluteString);
//允许跳转
decisionHandler(WKNavigationActionPolicyAllow);
//不允许跳转
//decisionHandler(WKNavigationActionPolicyCancel);
}
- 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:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler {
completionHandler(@"http");
}
// 确认框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler {
completionHandler(YES);
}
// 警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
completionHandler();
}
二 OC 与 JS 交互
WKWebview提供了API实现js交互 不需要借助JavaScriptCore或者webJavaScriptBridge。使用WKUserContentController实现js native交互。简单的说就是先注册约定好的方法,然后再调用。
JS 调用 OC 方法
- WKDelegateViewController.h文件代码
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
@protocol WKDelegate <NSObject>
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
@end
@interface WKDelegateViewController : UIViewController <WKScriptMessageHandler>
/** 代理 */
@property(nonatomic, weak) id<WKDelegate> delegate;
@end
- WKDelegateViewController.m 文件代码
#import "WKDelegateViewController.h"
@interface WKDelegateViewController ()
@end
@implementation WKDelegateViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if ([self.delegate respondsToSelector:@selector(userContentController:didReceiveScriptMessage:)]) {
[self.delegate userContentController:userContentController didReceiveScriptMessage:message];
}
}
@end
- 主文件代码
#import "WKWebViewController.h"
#import <WebKit/WebKit.h>
#import "WKDelegateViewController.h"
@interface WKWebViewController ()<WKUIDelegate, WKNavigationDelegate,WKScriptMessageHandler,WKDelegate>
@end
@implementation WKWebViewController {
WKWebView *_webView;
WKUserContentController *_userContentController;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self setupData];
[self drawUI];
}
- (void)dealloc {
NSLog(@"WKWebViewController dealloc");
//这里需要注意,前面增加过的方法一定要remove掉。
[_userContentController removeScriptMessageHandlerForName:@"sayhello"];
}
- (void)setupData {
self.view.backgroundColor = [UIColor whiteColor];
self.title = @"WKWebView";
}
- (void)drawUI {
// 配置环境
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
_userContentController = [[WKUserContentController alloc] init];
configuration.userContentController = _userContentController;
// 注册方法
WKDelegateViewController *delegateVC = [[WKDelegateViewController alloc] init];
delegateVC.delegate = self;
[_userContentController addScriptMessageHandler:delegateVC name:@"sayhello"];
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
_webView = webView;
webView.UIDelegate = self;
webView.navigationDelegate = self;
[self.view addSubview:webView];
NSString *path = [[NSBundle mainBundle] pathForResource:@"Company-2" ofType:@"html"];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:path]]];
}
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);
}
- h5代码
<!DOCTYPE html>
<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 日志:
注意点
-
addScriptMessageHandler
要和removeScriptMessageHandlerForName
配套出现,否则会造成内存泄漏。 - h5只能传一个参数,如果需要多个参数就需要用字典或者json组装。
OC 调用 JS 方法
代码如下
// 页面加载完成时调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
NSLog(@"didFinishNavigation");
//say()是JS方法名,completionHandler是异步回调block
[webView evaluateJavaScript:@"say()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"evaluateJavaScript %@",result);
}];
}
h5代码同上
本文参考IOS进阶之WKWebView,非常感谢该作者
项目连接地址
网友评论