为何使用WKWebView
App开发过程中不可避免的会用到加载网页,从iOS2开始,我们一直使用的是UIWebView
加载,这个网页加载器加载速度慢、占用内存多、优化困难,如果加载网页过多,还可能因为过度占用内存而被系统kill
掉。
iOS8以后,Apple推出了WebKit
,提供了替换UIWebView
的组件WKWebView
,毫无疑问WKWebView
将逐步取代笨重的UIWebView
。
WKWebView新特性
- 在性能、稳定性、功能方面有很大提升,网页加载速度快、内存占用少
- 支持更多的HTML5特性
- 与Safari相同的JavaScript引擎
- 官方宣称的高达60fps的滚动刷新率以及内置手势
- 增加加载进度属性:estimatedProgress
- 将UIWebViewDelegate与UIWebView重构成了14类与3个协议(查看苹果官方文档);
WKWebView使用说明
初始化方法分两种(记得导入#import <WebKit/WebKit.h>
)
// 默认初始化
- (instancetype)initWithFrame:(CGRect)frame;
// 根据对webview的相关配置,进行初始化
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
加载网页与HTML代码的方式与UIWebView相同
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];
[self.view addSubview:webView];
网页导航刷新相关属性及方法
// 是否可以后退
@property (nonatomic, readonly) BOOL canGoBack;
// 是否可以向前
@property (nonatomic, readonly) BOOL canGoForward;
// 是否正在加载
@property (nonatomic, readonly) BOOL loading;
// 加载进度,取值(0~1)
@property (nonatomic, readonly) double estimatedProgress;
// 是否允许左右划手势导航,默认不允许
@property (nonatomic) BOOL allowsBackForwardNavigationGestures;
// WKBackForwardList类型,访问历史列表,可以通过前进后退按钮访问,或者通过goToBackForwardListItem函数跳到指定页面
@property (nonatomic, readonly, strong) WKBackForwardList *backForwardList;
// 刷新
- (WKNavigation *)reload;
// 停止加载
- (void)stopLoading;
// 后退函数
- (WKNavigation *)goBack;
// 前进函数
- (WKNavigation *)goForward;
// 会比较网络数据是否有变化,没有变化则使用缓存,否则从新请求。
- (WKNavigation *)reloadFromOrigin;
// 跳转到某个指定历史页面
- (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item;
WKWebView代理方法
1.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;
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
2.WKUIDelegate
UI界面相关,原生控件支持,三种提示框:输入、确认、警告。首先将web提示框拦截然后再做处理。
// 创建一个新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;
// 输入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler;
// 确认框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
// 警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;
3.WKScriptMessageHandler
这个协议中包含一个必须实现的方法,这个方法是提高App与web端交互的关键,它可以直接将接收到的JS脚本转为OC或Swift对象。提供从网页中接收消息的回调方法。
// 从web界面中接收到一个脚本时调用
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
小技巧
- 自适应屏幕宽度
NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
WKUserContentController *wkUController = [[WKUserContentController alloc] init];
[wkUController addUserScript:wkUScript];
WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init];
wkWebConfig.userContentController = wkUController;
_web1 = [[WKWebView alloc] initWithFrame:CGRectNull configuration:wkWebConfig];
- 获取页面高度
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
[webView evaluateJavaScript:@"document.body.offsetHeight" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
_webHeight = [result doubleValue];
}];
}
- 图片自适应设备宽度
NSString *js = @"function imgAutoFit() { \
var imgs = document.getElementsByTagName('img'); \
for (var i = 0; i < imgs.length; ++i) {\
var img = imgs[i]; \
img.style.maxWidth = %f; \
} \
}";
js = [NSString stringWithFormat:js, [UIScreen mainScreen].bounds.size.width - 20];
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
WKUserContentController *wkUController = [[WKUserContentController alloc] init];
[wkUController addUserScript:wkUScript];
WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init];
wkWebConfig.userContentController = wkUController;
_webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 64, qScreenWidth, qScreenHeight - 64) configuration:wkWebConfig];