1.背景
WKWebView是苹果在iOS 8中引入的新组件,目的是给出一个新的高性能的WebView解决方案,摆脱过去 UIWebView的老、旧、笨重,特别是内存占用量巨大的问题,它使用Nitro JavaScript引擎,这意味着所有第三方浏览器运行JavaScript将会跟safari一样快。
WKWebView对比UIWebView
1. WKWebView的内存开销要比UIWebView小很多
2. 拥有高达60FPS滚动刷新率及内置手势
3. 支持了更多的HTML5特性
4. html页面和WKWebView交互更方便
5. Safari相同的JavaScript引擎
6. 提供常用的属性,如加载网页进度的属性estimatedProgress
2.基本使用
2.1. 初始化
除了-init初始化方法之外,WebKit还提供了另外一个初始化方法:-initWithFrame:configuration: ,第二个参数configuration提供了丰富的可配置属性,我们目前只用到了其中的WKPreferences,作用下面会说到
2.2. 加载请求
一共有4个加载api:
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL API_AVAILABLE(macosx(10.11), ios(9.0));
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL API_AVAILABLE(macosx(10.11), ios(9.0));
我们项目中用到的是前2个api,第一个api主要是用来访问iOS9及以上设备的网络请求和iOS9以下的所有请求,第二个api用于iOS9及以上设备的本地资源访问。
2.3. 代理
WKWebview主要涉及到两个delegate:
WKNavigationDelegate和WKUIDelegateWKNavigationDelegate一般涉及到页面加载状态的回调:
// 页面开始加载时调用
- (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;
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
WKUIDelegate一般用于JS调用客户端弹出框(警告框、确认框、输入框)的时候进行相应的自定义操作,项目中没有涉及到,就不多阐述。
2.4. 客户端与JS交互(WKWebVIew调用js)
区别于UIWebView中的-stringByEvaluatingJavaScriptFromString:同步方法,WKWebView中调用js的api为-evaluateJavaScript:completionHandler: ,completionHandler是异步回调的block
2.5. JS与客户端交互
首先客户端通过WKUserContentController类的-addScriptMessageHandler:name:方法添加响应指定name方法的代理对象,然后js通过以下方式调用oc方法:window.webkit.messageHandlers..postMessage(),js发起后,代理方法-userContentController:didReceiveScriptMessage:被调用。另:addScriptMessageHandler之后要记得removeScriptMessageHandlerForName。
3.遇到的问题:
3.1. 与UIWebview的webView:shouldStartLoadWithRequest:navigationType:代理相对应的代理方法是webView:decidePolicyForNavigationAction:decisionHandler:而不是webView:didStartProvisionalNavigation:
3.2. 加载本地html问题
ios9及以上加载本地html资源的时候不能通过loadRequest:这个api进行请求,只能通过iOS 9新接口 -loadFIleURL:allowingReadAccessToURL: ,此处需要注意传入的第一个参数url必须得是fileUrl格式的,即前缀得是file:// 格式的本地路径,第二个参数要是访问文件的文件夹路径。
iOS 9 以下解决方案:先将本地 HTML 文件的数据 copy 到 tmp 目录中,然后再使用 loadRequest 来加载。我们项目中则是通过移动整个webResource文件夹到tmp目录解决。 考虑到iOS8中WKWebView存在较多问题,后来我们在iOS中统一采用UIWebview方案。
3.3. 跨域问题
跨域访问,简单来说就是 A 网站的 javascript 代码试图访问 B 网站,包括提交内容和获取内容。由于安全原因,跨域访问是被各大浏览器所默认禁止的。
CORS(一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing))允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
WKKit默认强制实现CORS,在不涉及服务端改动的情况下通过KVC修改属性值解决[config.preferences setValue:@"TRUE" forKey:@"allowFileAccessFromFileURLs"]