一、WKWebView
使用WKWebView替换UIWebView可以大量减少运行内存。用法与UIWebView相似,但需导入<WebKit/WebKit.h>头文件,另外代理的名字为 navigationDelegate,用的时候遵守下 WKNavigationDelegate 协议。
#import "ViewController.h"
#import <WebKit/WebKit.h>
@interface ViewController ()<WKNavigationDelegate>
@property(nonatomic,strong)WKWebView *webView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setupUI];
[self beginRequest];
}
-(void)setupUI{
//创建webView
_webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
_webView.navigationDelegate = self;
[self.view addSubview:_webView];
}
-(void)beginRequest{
//创建请求并开始加载网页
NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://www.baidu.com"]];
[webView loadRequest:urlRequest];
}
@end
用到的几个代理方法:
#pragma mark UIWebViewDelegate
-(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
NSLog(@"开始请求");
}
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
NSLog(@"请求结束");
}
-(void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error{
NSLog(@"请求失败");
}
同样加载 https://www.baidu.com 与使用UIWebView的内存对比:
WKWebView:
UIWebView:
可见节省了很多内存。
二、处理WKWebView返回403、404
1、现有需求如下:
当webView加载失败时,不显示webView,加载成功显示webView
一般的失败,如随便输入不存在的地址,可以在代理方法中响应到:
-(void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error{
NSLog(@"请求失败,不显示webView");
}
但如果请求返回的是403、404时,webView不认为这是请求失败,不调用上面的代理方法。进而显示出来:
2、解决办法
1)法1
查找文档没有找到响应处理办法,想着只好自己再发送一个请求判断是否是403、404,经网络查找,发现UIWebView可以用loadData方法,先发送请求保存为data,如果返回是403、404就不显示。
UIWebView处理403、404出处: http://blog.csdn.net/waterforest_pang/article/details/8599322
于是在WKWebView中找到了对应的方法:
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL
可惜的是该方法iOS9以后才开始支持,对于iOS8只能用丑陋点的办法了。
由于加载URL是个耗时操作,因此用NSOperation开一个子线程调用,将先前viewDidLoad中的beginRequest方法修改如下:
-(void)beginRequest{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(requestURL) object:nil];
[queue addOperation:op];
}
-(void)requestURL{
NSURLRequest *urlRequest = [[NSURLRequest alloc]initWithURL:[NSURL URLWithString:@"http://api.2us.cn:3000/index"]];
NSHTTPURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:nil];
if (response.statusCode == 404 || response.statusCode == 403 || response == nil) {
// 请求失败,回主线程更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"请求失败,不显示webView");
}];
return;
}
__weak __typeof__(self) weakSelf = self;
//请求成功,回主线程更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
if(IOS9_OR_LATER){ //iOS9后直接调用loadData方法,不用再请求
[weakSelf.webView loadData:data MIMEType:@"text/html" characterEncodingName:@"UTF-8" baseURL:[urlRequest URL]];
}else{ //iOS8只好再请求
[weakSelf.webView loadRequest:urlRequest];
}
}];
}
//备注 : 本项目最低部署在iOS8,因此采用上述判断
//另附宏定义 #define IOS9_OR_LATER ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9.0)
2)法2
刚刚解决的办法实在太丑陋了,于是乎偶然间发现了WKWebView框架中的一个回调
在此处发现的:http://blog.csdn.net/magiczyj/article/details/56280268
//服务器返回200以外的状态码时,都调用请求失败的方法,从而可以做一些处理。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
if (((NSHTTPURLResponse *)navigationResponse.response).statusCode == 200) {
decisionHandler (WKNavigationResponsePolicyAllow);
}else {
decisionHandler(WKNavigationResponsePolicyCancel);
}
}
这样就轻松解决了
另外附上WKWebView框架的浅析说明:http://www.cnblogs.com/Jenaral/p/5738769.html
三、网页加载进度条的思路:
在WKWebView中发现了一个可爱的属性:
@property (nonatomic, readonly) double estimatedProgress;
预估加载进度,这就好办了,直接利用KVO监听它的改变,就可以做一个进度条了
-(void)setupUI{
//创建webView
_webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
_webView.navigationDelegate = self;
[self.view addSubview:_webView];
//加一个监听
[_webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
}
//KVO监听调用
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
NSLog(@"%f",_webView.estimatedProgress); //可以用它搞事情了~
}
//切记加了监听要释放,,否则会奔溃
-(void)dealloc{
[_webView removeObserver:self forKeyPath:@"estimatedProgress"];
}