UIWebView自iOS2就有,WKWebView从iOS8才有,毫无疑问WKWebView将逐步取代笨重的UIWebView。通过简单的测试即可发现UIWebView占用过多内存,且内存峰值更是夸张。WKWebView网页加载速度也有提升,但是并不像内存那样提升那么多
基本属性:网页加载进度、网页标题,这些网页的最最基本的属性,终于齐了。
前进后退手势:在UIWebView实现过这个功能的我,深知此功能之复杂,当看到这个的时候,整个人都鸟肌了。
WKPreferences:对应WebView的WebViewPreference,相比UIWebView,增加了禁用JavaScript功能,但没有无图模式,差评。
WKUserContentController:JS通讯相关,App注入JS时,可以指定时机(加载开始或加载结束)和范围(MainFrame或所有Frame);另外内置JS Bridge。
WKProcessPool:和多进程模型相关,目前功能未知。
WKBackForwardList:前进后退列表,良心好评。
WKNavigationDelegate:类似于WebView里的WebFrameLoadDelegate,功能稍稍阉割了下,但基本能用。
WKUIDelegate:UI相关的回调。如新窗口打开、页面alert弹框等的处理回调。
更多细节,可以查看WebKit Objective-C Framework Reference。
UIProgressView *progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
[self.view addSubview:progressView];
self.progressView = progressView;
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
//偏好设置
config.preferences = [[WKPreferences alloc] init];
config.preferences.minimumFontSize = 10;
config.preferences.javaScriptEnabled = YES;
//表示不能自动打开窗口
config.preferences.javaScriptCanOpenWindowsAutomatically = NO;
//web内容处理池,由于没有属性可以设置,也没有方法可以调用,不用手动创建
config.processPool = [[WKProcessPool alloc] init];
WKWebView *web2 = [[WKWebView alloc] init];//WithFrame:self.view.bounds configuration:config]; //WithFrame:self.view.bounds
web2.navigationDelegate = self;
// web2.UIDelegate = self;
[web2 addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:NULL];
[self.view addSubview:web2];
self.web2 = web2;
[web2.configuration.userContentController addScriptMessageHandler:self name:@"closeMe"];
[progressView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.right.equalTo(self.view);
}];
[web2 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(progressView.mas_bottom);
make.left.right.bottom.equalTo(self.view);
}];
#define mark 设置加载进度条
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if ([keyPath isEqualToString:@"estimatedProgress"] && object == self.web2) {
self.progressView.alpha = 1.0;
[self.progressView setProgress:self.web2.estimatedProgress animated:YES];
if (self.web2.estimatedProgress >= 1.0) {
[UIView animateWithDuration:0.3 delay:0.3 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.progressView.alpha = 0.0;
} completion:^(BOOL finished) {
[self.progressView setProgress:0.0f animated:YES];
}];
}
}else{
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#pragma mark WKNavigationDelegate
//2.页面开始加载
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
NSLog(@"%s",__func__);
}
///5.页面跳转完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
NSLog(@"%s",__func__);
}
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{
NSLog(@"%s----%@",__func__,error);
}
//证书设置
// 这与用于授权验证的API,与AFN、UIWebView的授权验证API是一样的
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler{
NSLog(@"%s",__func__);
// NSString *hostName = webView.URL.host;
//
// NSString *authenticationMethod = [[challenge protectionSpace] authenticationMethod];
// if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodDefault]
// || [authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]
// || [authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPDigest]) {
//
// NSString *title = @"Authentication Challenge";
// NSString *message = [NSString stringWithFormat:@"%@ requires user name and password", hostName];
// UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
// [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
// textField.placeholder = @"User";
// //textField.secureTextEntry = YES;
// }];
// [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
// textField.placeholder = @"Password";
// textField.secureTextEntry = YES;
// }];
// [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
//
// NSString *userName = ((UITextField *)alertController.textFields[0]).text;
// NSString *password = ((UITextField *)alertController.textFields[1]).text;
//
// NSURLCredential *credential = [[NSURLCredential alloc] initWithUser:userName password:password persistence:NSURLCredentialPersistenceNone];
//
// completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
//
// }]];
// [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
// completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
// }]];
// dispatch_async(dispatch_get_main_queue(), ^{
// [self presentViewController:alertController animated:YES completion:^{}];
// });
//
// }
// else if ([authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// // needs this handling on iOS 9
// completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
// // or, see also http://qiita.com/niwatako/items/9ae602cb173625b4530a#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89
// }
// else {
// completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
// }
NSURLProtectionSpace *protectionSpace = [challenge protectionSpace];
id<NSURLAuthenticationChallengeSender> sender = [challenge sender];
if ([[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust])
{
SecTrustRef trust = [[challenge protectionSpace] serverTrust];
NSURLCredential *credential = [[NSURLCredential alloc] initWithTrust:trust];
// [sender useCredential:credential forAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, credential);
}
else
{
// [sender performDefaultHandlingForAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
}
}
// * 在收到响应后,决定是否跳转
// *
// * @param webView 实现该代理的webview
// * @param navigationResponse 当前navigation
// * @param decisionHandler 是否跳转block
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
decisionHandler(WKNavigationResponsePolicyAllow);
}
// 在收到服务器的响应头,根据response相关信息,决定是否跳转。decisionHandler必须调用,来决定是否跳转,参数WKNavigationActionPolicyCancel取消跳转,WKNavigationActionPolicyAllow允许跳转
1.
// * 在发送请求之前,决定是否跳转
// *
// * @param webView 实现该代理的webview
// * @param navigationAction 当前navigation
// * @param decisionHandler 是否调转block
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
NSLog(@"%s",__func__);
decisionHandler(WKNavigationActionPolicyAllow);
}
//4.开始获取页面内容时返回
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{
NSLog(@"%s",__func__);
}
//页面跳转失败
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error{
NSLog(@"%s---=====%@",__func__,error);
}
#pragma mark WKScriptMessageHandler{
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
NSLog(@"名称%@ --方法%@",message.name,message.body);
}
- (void)dealloc{
if ([self isViewLoaded]) {
[self.web2 removeObserver:self forKeyPath:@"estimatedProgress"];
}
self.web2.navigationDelegate = nil;
self.web2.UIDelegate = nil;
}
#pragma mark UIWebViewDelegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
NSLog(@"%s,-----%@",__func__,request.URL.absoluteString);
NSLog(@"Did start loading: %@ auth:%d", [[request URL] absoluteString], _authenticated);
if (!_authenticated) {
_authenticated = NO;
_urlConnection = [[NSURLConnection alloc] initWithRequest:_requset delegate:self];
[_urlConnection start];
return NO;
}
return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView{
NSLog(@"%s",__func__);
[UMProgressHUD show];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView{
NSLog(@"%s",__func__);
[UMProgressHUD dismiss];
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
NSLog(@"%@",error);
[UMProgressHUD dismiss];
}
//通过connection来设置证书
#pragma mark - NURLConnection delegate
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
{
NSLog(@"WebController Got auth challange via NSURLConnection");
if ([challenge previousFailureCount] == 0)
{
_authenticated = YES;
NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
} else
{
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
}
// We use this method is to accept an untrusted site which unfortunately we need to do, as our PVM servers are self signed.
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
#pragma mark - NSURLConnectionDataDelegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
{
NSLog(@"WebController received response via NSURLConnection");
// remake a webview call now that authentication has passed ok.
_authenticated = YES;
[_web1 loadRequest:_requset];
// Cancel the URL connection otherwise we double up (webview + url connection, same url = no good!)
[_urlConnection cancel];
}
总的来说,苹果鼓起勇气,第一次开放了如此多的接口,但却仍然不够彻底。这些功能,在UIWebView时代,都可以用私有API解决;而使用了WKWebView之后,在某些方面,依然显得不够用的样子——比方说HTTP和HTTPS验证,仍然没有公开接口可以处理;另外WebResourceLoadDelegate的缺失,也是一大痛点。
2014.7.8 iOS8 beta3 更新Webkit.framework,如下:
同步调用的JS接口。和-[UIWebView stringByEvaluatingJavaScriptFromString:]相比,JS结果是异步而非同步返回的,返回结果为id而非NSString。
WKNavigationDelegate增加了验证回调接口,可以解决HTTP和HTTPS验证问题