在iOS开发中,调用web页面时,最常用的就是
UIWebView控件。但在使用过程中,也让大家觉得很不爽,就是内存爆增。
自从iOS8.0以后有了WKWebView,这种情况就好转了。因为在使用WKWebView的过程中基本上内存保持不变。
WKWebView 是现代 WebKit API在 iOS 8和 OS X Yosemite应用中的核心部分。
WKWebView 代替了 UIKit中的 UIWebView和 AppKit中的 WebView,提供了统一的跨双平台 API。
WKWebView的特点:
1 拥有 60fps滚动刷新率
2 内置手势
3 高效的 app和 web信息交换通道
4 和 Safari相同的 JavaScript引擎
5 性能高,稳定性好,占用的内存比较小
下面两图分别是使用WKWebView、UIWebView时的内存情况
WKWebView怎么使用呢?话不多说,直接上码。
1、导入头文件
- #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0
- #import <WebKit/WebKit.h>
- #endif
在这里我定义成属性使用,同时使用getter方法进行实例化,实例化后记得要添加到父视图。
- @interface WKWebViewVC () <WKUIDelegate, WKNavigationDelegate>
- @property (nonatomic, strong) WKWebView *webview;
- @end
- - (WKWebView *)webview
- {
- if (_webview == nil)
- {
- _webview = [[WKWebView alloc] init];
- _webview.frame = self.view.bounds;
- _webview.backgroundColor = [UIColor clearColor];
- _webview.UIDelegate = self;
- _webview.navigationDelegate = self;
- }
- return _webview;
- }
- [self.view addSubview:self.webview];
3-1、WKUIDelegate代理方法
UI界面相关,原生控件支持,三种提示框:输入、确认、警告。首先将web提示框拦截然后再做处理
- /// 创建一个新的WebView
- - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
- {
- NSLog(@"加载:打开内部链接");
- // 打开内部链接
- return self.webview;
- // 不打开
- // return nil;
- }
- /// 输入框
- - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler
- {
- NSLog(@"加载:输入框提示");
- completionHandler(@"Client Not handler");
- }
- /// 确认框
- - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler
- {
- NSLog(@"加载:确认框提示");
- // js 里面的alert实现,如果不实现,网页的alert函数无效
- UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message
- message:nil
- preferredStyle:UIAlertControllerStyleAlert];
- [alertController addAction:[UIAlertAction actionWithTitle:@"确定"
- style:UIAlertActionStyleDefault
- handler:^(UIAlertAction *action) {
- completionHandler(YES);
- }]];
- [alertController addAction:[UIAlertAction actionWithTitle:@"取消"
- style:UIAlertActionStyleCancel
- handler:^(UIAlertAction *action){
- completionHandler(NO);
- }]];
- [self presentViewController:alertController animated:YES completion:^{}];
- }
- /// 警告框
- - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
- {
- NSLog(@"加载:警告框提示");
- // js 里面的alert实现,如果不实现,网页的alert函数无效
- UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message
- message:nil
- preferredStyle:UIAlertControllerStyleAlert];
- [alertController addAction:[UIAlertAction actionWithTitle:@"确定"
- style:UIAlertActionStyleCancel
- handler:^(UIAlertAction *action) {
- completionHandler();
- }]];
- [self presentViewController:alertController animated:YES completion:^{}];
- }
追踪加载过程,有是否允许加载、开始加载、加载完成、加载失败
- /// 接收到服务器跳转请求之后调用 (服务器端redirect),不一定调用
- - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation
- {
- [UIApplication sharedApplication].networkActivityIndicatorVisible = NO ;
- NSLog(@"加载:服务器跳转");
- }
- /// 1 在发送请求之前,决定是否跳转(注:不加上decisionHandler回调会造成闪退)
- - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
- {
- [UIApplication sharedApplication].networkActivityIndicatorVisible = YES ;
- NSLog(@"加载:发送请求是否跳转");
- WKNavigationActionPolicy actionPolicy = WKNavigationActionPolicyAllow;
- // 这句是必须加上的,不然会异常
- decisionHandler(actionPolicy);
- }
- /// 2 页面开始加载
- - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation
- {
- [UIApplication sharedApplication].networkActivityIndicatorVisible = YES ;
- NSLog(@"加载:开始");
- }
- /// 3 在收到服务器的响应头,根据response相关信息,决定是否跳转。decisionHandler必须调用,来决定是否跳转,参数WKNavigationActionPolicyCancel取消跳转,WKNavigationActionPolicyAllow允许跳转(注:不加上decisionHandler回调会造成闪退)
- - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
- {
- NSLog(@"加载:服务器响应头");
- WKNavigationResponsePolicy responsePolicy = WKNavigationResponsePolicyAllow;
- // 这句是必须加上的,不然会异常
- decisionHandler(responsePolicy);
- }
- /// 4 开始获取到网页内容时返回
- - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation
- {
- [UIApplication sharedApplication].networkActivityIndicatorVisible = YES ;
- NSLog(@"加载:开始获取信息");
- }
- /// 5 页面加载完成之后调用
- - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
- {
- [UIApplication sharedApplication].networkActivityIndicatorVisible = NO ;
- NSLog(@"加载:完成");
- }
- /// 页面加载失败时调用
- - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation
- {
- [UIApplication sharedApplication].networkActivityIndicatorVisible = NO ;
- NSLog(@"加载:失败");
- }
4、打开web页面,或停止加载web页面
- - (void)startClick
- {
- if ([_webview isLoading])
- {
- return;
- }
- NSURL *url = [NSURL URLWithString:@"http://www.hao123.com"];
- NSURLRequest *request = [NSURLRequest requestWithURL:url];
- [_webview loadRequest:request];
- NSLog(@"加载:启动");
- }
- - (void)stopClick
- {
- if ([_webview isLoading])
- {
- [_webview stopLoading];
- [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
- NSLog(@"加载:停止");
- }
- }
注意:
1、视图控制器释放时,注意内存管理。
- - (void)dealloc
- {
- _webview.UIDelegate = nil;
- _webview.navigationDelegate = nil;
- [_webview loadHTMLString:@"" baseURL:nil];
- [_webview stopLoading];
- [_webview removeFromSuperview];
- [[NSURLCache sharedURLCache] removeAllCachedResponses];
- [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
- _webview = nil;
- }
2、WKWebView有个监视加载进度的属性"estimatedProgress"
注意:kvo使用时path名称必须是"estimatedProgress",或者"NSStringFromSelector(@selector(estimatedProgress))
",否则会造成异常闪退。
- #pragma mark - 通知方法
- - (void)observeProgress
- {
- // 进度条监视
- CGFloat progress = self.webview.estimatedProgress;
- NSLog(@"加载进度:%.2f", progress);
- [self.webview addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
- }
- - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
- {
- // 进度条
- if ([@"estimatedProgress" isEqualToString:keyPath])
- {
- CGFloat progress = self.webview.estimatedProgress;
- NSLog(@"正在加载中,加载进度:%.2f", progress);
- // 初始和终止状态
- if (progress == 0.0)
- {
- NSLog(@"加载开始,进度条值:0.0");
- }
- else if (progress == 1.0)
- {
- NSLog(@"加载结束,进度条值:1.0");
- // 1秒后隐藏
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- // 再次判断,防止正在加载时有其他操作
- if (progress == 1.0)
- {
- NSLog(@"加载结束,进度条值:1.0(异步)");
- }
- });
- }
- }
- }
- 2016-09-30 17:10:47.121 DemoWebViewOptimize[16698:308353] 加载进度:0.00
- 2016-09-30 17:10:48.148 DemoWebViewOptimize[16698:308353] 正在加载中,加载进度:0.10
- 2016-09-30 17:10:48.148 DemoWebViewOptimize[16698:308353] 加载:启动
- 2016-09-30 17:10:48.160 DemoWebViewOptimize[16698:308353] 加载:发送请求是否跳转
- 2016-09-30 17:10:48.170 DemoWebViewOptimize[16698:308353] 加载:开始
- 2016-09-30 17:10:48.273 DemoWebViewOptimize[16698:308353] 加载:发送请求是否跳转
- 2016-09-30 17:10:48.274 DemoWebViewOptimize[16698:308353] 加载:服务器跳转
- 2016-09-30 17:10:48.326 DemoWebViewOptimize[16698:308353] 加载:发送请求是否跳转
- 2016-09-30 17:10:48.327 DemoWebViewOptimize[16698:308353] 加载:服务器跳转
- 2016-09-30 17:10:48.468 DemoWebViewOptimize[16698:308353] 加载:服务器响应头
- 2016-09-30 17:10:48.469 DemoWebViewOptimize[16698:308353] 正在加载中,加载进度:0.30
- 2016-09-30 17:10:48.527 DemoWebViewOptimize[16698:308353] 加载:开始获取信息
- 2016-09-30 17:10:48.635 DemoWebViewOptimize[16698:308353] 加载:发送请求是否跳转
- 2016-09-30 17:10:48.692 DemoWebViewOptimize[16698:308353] 正在加载中,加载进度:0.85
- 2016-09-30 17:10:48.713 DemoWebViewOptimize[16698:308353] 加载:服务器响应头
- 2016-09-30 17:10:48.923 DemoWebViewOptimize[16698:308353] 正在加载中,加载进度:0.90
- 2016-09-30 17:10:48.989 DemoWebViewOptimize[16698:308353] 加载:发送请求是否跳转
- 2016-09-30 17:10:48.991 DemoWebViewOptimize[16698:308353] 加载:服务器响应头
- 2016-09-30 17:10:49.008 DemoWebViewOptimize[16698:308353] 加载:发送请求是否跳转
- 2016-09-30 17:10:49.188 DemoWebViewOptimize[16698:308353] 正在加载中,加载进度:0.90
- 2016-09-30 17:10:49.197 DemoWebViewOptimize[16698:308353] 加载:服务器响应头
- 2016-09-30 17:10:49.198 DemoWebViewOptimize[16698:308353] 正在加载中,加载进度:1.00
- 2016-09-30 17:10:49.199 DemoWebViewOptimize[16698:308353] 加载结束,进度条值:1.0
- 2016-09-30 17:10:49.199 DemoWebViewOptimize[16698:308353] 加载:完成
- 2016-09-30 17:10:49.328 DemoWebViewOptimize[16698:308353] 加载:发送请求是否跳转
- 2016-09-30 17:10:49.384 DemoWebViewOptimize[16698:308353] 加载:服务器响应头
- 2016-09-30 17:10:49.855 DemoWebViewOptimize[16698:308353] 加载:发送请求是否跳转
- 2016-09-30 17:10:49.900 DemoWebViewOptimize[16698:308353] 加载:服务器响应头
- 2016-09-30 17:10:50.293 DemoWebViewOptimize[16698:308353] 加载结束,进度条值:1.0(异步)
注意:
代理方法使用说明
- // 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;
- // 2 WKNavigtionDelegate来进行页面跳转
- // 接收到服务器跳转请求之后再执行
- - (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;
- // 3 WKUIDelegate(PS:WKUIDelegate主要是做跟网页交互的,可以显示javascript的一些alert或者Action,看起来跟自己做的一样的.)
- // 创建一个新的WebVeiw
- - (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;
- // WebVeiw关闭(9.0中的新方法)
- - (void)webViewDidClose:(WKWebView *)webView NS_AVAILABLE(10_11, 9_0);
- // 显示一个JS的Alert(与JS交互)
- - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;
- // 弹出一个输入框(与JS交互的)
- - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler;
- // 显示一个确认框(JS的)
- - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;