WKWebView 和UIWebView、网页缓存、网路请求缓存

网页缓存:https://www.jianshu.com/p/f3019d511f36、https://blog.csdn.net/leikezhu1981/article/details/68491249
网路请求缓存:https://www.cnblogs.com/wendingding/p/3950198.html
//
//ViewController.m
//WebView
//
//Created by lambo on 2017/1/17.
//Copyright ? 2017年 cn.lr. All rights reserved.
//

/*webview 自动计算内容高度----------
//第一种方法

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
CGFloat webViewHeight=[webView.scrollView contentSize].height;
CGRect newFrame = webView.frame;
newFrame.size.height = webViewHeight;
webView.frame = newFrame;
_webTablewView.contentSize = CGSizeMake(320, newFrame.size.height + 64 + KWIDTH - 100);
}
//2.执行js语句 直接获取html文档的dom高度

- (void)webViewDidFinishLoad:(UIWebView *)webView{
CGFloatwebViewHeight =[[webViewstringByEvaluatingJavaScriptFromString:@document.body.offsetHeight]floatValue];
// CGFloat webViewHeight= [[webViewstringByEvaluatingJavaScriptFromString:@document.body.scrollHeight]floatValue];
CGRectnewFrame = webView.frame;
newFrame.size.height= webViewHeight;
webView.frame= newFrame;
}
//方法3.先将UIWebView的高度设为最小,然后再使用sizeThatFits就会返回刚好合适的大小

-(void)webViewDidFinishLoad:(UIWebView*)webVie{
CGSize actualSize = [webView sizeThatFits:CGSizeZero];
CGRect newFrame = webView.frame;
newFrame.size.height = actualSize.height;
webView.frame = newFrame;
}
//方法4.遍历webview子视图 获取UIWebDocumentView高度即实际高度

-(void)webViewDidFinishLoad:(UIWebView *)webView{
CGFloat webViewHeight = 0.0f;
if([webView.subviews count] > 0)
{
UIView *scrollerView = webView.subviews[0];
if([scrollerView.subviews count] >
0)
{
UIView *webDocView = scrollerView.subviews.lastObject;
if ([webDocView isKindOfClass:[NSClassFromString(@UIWebDocumentView)class]])
{
webViewHeight = webDocView.frame.size.height; //获取文档的高度
webView.frame=webDocView.frame;
//更新UIWebView 的高度
}
}
}
}
*/

#import "ViewController.h"

#import //MKWebKit要使用这个框架

@interfaceViewController ()

@end

@implementation ViewController
/**webView中的属性==========================

UIWebview 会自动保存上一次的cookie,WKWebview不会。
代理属性重点需要知道代理方法的使用
@property (nullable, nonatomic, assign) id delegate;

这个是webView内部的scrollView只读,但是利用这个属性,设置scrollView的代理,就可以控制整个webView的滚动事件
@property(nonatomic, readonly, strong) UIScrollView *scrollView;

webView的请求,这个属性一般在整个加载完成后才能拿到
@property (nullable, nonatomic, readonly, strong) NSURLRequest *request;

A Boolean value indicating whether the receiver can move backward. (read-only)
If YES, able to move backward; otherwise, NO.
如果这个属性为YES,才能后退
@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack;

A Boolean value indicating whether the receiver can move forward. (read-only)
If YES, able to move forward; otherwise, NO.
如果这个属性为YES,才能前进
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward;

A Boolean value indicating whether the receiver is done loading content. (read-only)
If YES, the receiver is still loading content; otherwise, NO.
这个属性很好用,如果为YES证明webView还在加载数据,所有数据加载完毕后,webView就会为No
@property (nonatomic, readonly, getter=isLoading) BOOL loading;

A Boolean value determining whether the webpage scales to fit the view and the user can change the scale.
If YES, the webpage is scaled to fit and the user can zoom in and zoom out. If NO, user zooming is disabled. The default value is NO.
YES代表网页可以缩放,NO代表不可以缩放
@property (nonatomic) BOOL scalesPageToFit;

设置某些数据变为链接形式,这个枚举可以设置如电话号,地址,邮箱等转化为链接
@property (nonatomic) UIDataDetectorTypes dataDetectorTypes NS_AVAILABLE_IOS(3_0);

iPhone Safari defaults to NO. iPad Safari defaults to YES
设置是否使用内联播放器播放视频
@property (nonatomic) BOOL allowsInlineMediaPlayback NS_AVAILABLE_IOS(4_0);

iPhone and iPad Safari both default to YES
设置视频是否自动播放
@property (nonatomic) BOOL mediaPlaybackRequiresUserAction NS_AVAILABLE_IOS(4_0);

iPhone and iPad Safari both default to YES
设置音频播放是否支持ari play功能
@property (nonatomic) BOOL mediaPlaybackAllowsAirPlay NS_AVAILABLE_IOS(5_0);

iPhone and iPad Safari both default to NO
设置是否将数据加载入内存后渲染界面
@property (nonatomic) BOOL suppressesIncrementalRendering NS_AVAILABLE_IOS(6_0);

default is YES
设置用户是否能打开keyboard交互
@property (nonatomic) BOOL keyboardDisplayRequiresUserAction NS_AVAILABLE_IOS(6_0);

IOS7以后的新特性
这个属性用来设置一种模式,当网页的大小超出view时,将网页以翻页的效果展示,枚举如下:
@property (nonatomic) UIWebPaginationMode paginationMode NS_AVAILABLE_IOS(7_0);
typedef NS_ENUM(NSInteger, UIWebPaginationMode) {
UIWebPaginationModeUnpaginated, //不使用翻页效果
UIWebPaginationModeLeftToRight, //将网页超出部分分页,从左向右进行翻页
UIWebPaginationModeTopToBottom, //将网页超出部分分页,从上向下进行翻页
UIWebPaginationModeBottomToTop, //将网页超出部分分页,从下向上进行翻页
UIWebPaginationModeRightToLeft //将网页超出部分分页,从右向左进行翻页
};
This property determines whether certain CSS properties regarding column- and page-breaking are honored or ignored.
这个属性决定CSS的属性分页是可用还是忽略。默认是UIWebPaginationBreakingModePage
@property (nonatomic) UIWebPaginationBreakingMode paginationBreakingMode NS_AVAILABLE_IOS(7_0);

设置每一页的长度
@property (nonatomic) CGFloat pageLength NS_AVAILABLE_IOS(7_0);

设置每一页的间距
@property (nonatomic) CGFloat gapBetweenPages NS_AVAILABLE_IOS(7_0);

获取页数
@property (nonatomic, readonly) NSUInteger pageCount NS_AVAILABLE_IOS(7_0);

==============================*/

/**webView的代理方法==========================
// 加载Data数据创建一个webView
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL

// 加载本地HTML字符串(字符串中的内容是html网页代码)用webView调用这个方法
- (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL

// 加载一个请求创建一个webView,吧请求加在到webview网页中
- (void)loadRequest:(NSURLRequest *)request

// 刷新网页
- (void)reload;

// 停止网页加载内容
- (void)stopLoading;

// 后退
- (void)goBack;

// 前进
- (void)goForward;

// 执行JS方法,这个方法可以返回整个网页html代码,
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script
======================================*/


- (void)viewDidLoad {
[super viewDidLoad];


UIWebView *web=[[UIWebViewalloc]initWithFrame:self.view.frame];
[self.viewaddSubview:web];
[web loadHTMLString:@"sssssssssss
"baseURL:nil]; //加载html


////==========/webView的使用===============

//UIWebView *webView = [[UIWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
//self.view = webView;
//webView.delegate=self;
//NSURL *url = [[NSBundle mainBundle] URLForResource:@"Untitled.html" withExtension:nil]; //从本地获取的URL
//
NSURL *url = [NSURL URLWithString:@"https:/baidu.com"]; //从网络获取的URL
//NSURLRequest *request = [NSURLRequest requestWithURL:url]; //默认超时60秒

NSURLRequest *request=[NSURLRequestrequestWithURL:urlcachePolicy:NSURLRequestUseProtocolCachePolicytimeoutInterval:3]; //带超时时间的请求

//[webView loadRequest:request];

//===================================

//=====WKWebview===直接加载js,不是通过URL请求加载//方式4============


// 图片缩放的js代码myFunction()
NSString *js =@"returndocument.getElementsByTagName('p')[0].innerHTML";
//NSString *js = @"myFunction(); ";
// 根据JS字符串初始化WKUserScript对象
WKUserScript *script = [[WKUserScript alloc]initWithSource:js injectionTime:WKUserScript InjectionTimeAtDocumentStartforMainFrameOnly:YES];
//WKUserContentController *userContentController = [[WKUserContentController alloc] init];

创建网页配置对象,根据生成的WKUserScript对象,初始化WKWebViewConfiguration
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc]init];
[config.userContentController addUserScript:script];
// 创建设置对象
WKPreferences *preference = [[WKPreferencesalloc]init];
// 设置网页字体大小
preference.minimumFontSize =30;
// 设置偏好设置对象
config.preferences = preference;
//创建WKWebView
WKWebView* webView = [[WKWebViewalloc]initWithFrame:self.view.bounds configuration:config];
webView.navigationDelegate=self;
self.view=webView;
//NSURL *url = [[NSBundle mainBundle] URLForResource:@"Untitled.html" withExtension:nil]; //从本地获取的URL
//======这个也可以获取本地路径 //网页文件全路径
//NSString*filePath = [[NSBundle mainBundle] pathForResource:@"Untitled.html" ofType:nil];
//// 创建URL对象:指定要加载资源的路径
// =======NSURL *URL = [NSURL fileURLWithPath:filePath];
//直接加在html==============
[webView loadHTMLString:@"这是爱鱼app,iOS版本我的老家就住在这个屯我是这个屯里土生图章的人啊这是爱鱼app,iOS版本我的老家就住在这个屯我是这个屯里土生图章的人啊这是爱鱼app,iOS版本我的老家就住在这个屯我是这个屯里土生图章的人啊这是爱鱼app,iOS版本我的老家就住在这个屯我是这个屯里土生图章的人啊这是爱鱼app,iOS版本我的老家就住在这个屯我是这个屯里土生图章的人啊这是爱鱼app,iOS版本我的老家就住在这个屯我是这个屯里土生图章的人啊这是爱鱼app,iOS版本我的老家就住在这个屯我是这个屯里土生图章的人啊
"baseURL:nil];

//=========WKWebview========//方式3============
//WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc] init];
//WKWebView*webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
//NSURL *url = [[NSBundle mainBundle] URLForResource:@"Untitled.html" withExtension:nil]; //从本地获取的URL
//self.view = webView;
//webView.UIDelegate=self;
//webView.navigationDelegate=self;
//[webView loadRequest:[NSURLRequest requestWithURL:url]];


////====22======WKWebview 的使用=============

//// js配置
//WKUserContentController *userContentController = [[WKUserContentController alloc] init];
//[userContentController addScriptMessageHandler:self name:@"webViewLoadStart"];
//[userContentController addScriptMessageHandler:self name:@"webViewLoadFinish"];
//[userContentController addScriptMessageHandler:self name:@"webViewLogout"];
//[userContentController addScriptMessageHandler:self name:@"webViewSuccess"];
//
//// WKWebView的配置
//WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
//configuration.userContentController = userContentController;
//
//// 显示WKWebView
//WKWebView * webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
//webView.UIDelegate = self; // 设置WKUIDelegate代理
//webView.navigationDelegate = self; // 设置WKNavigationDelegate代理


////====11======WKWebview 的使用=============
//
//WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
//self.view = webView;
//webView.UIDelegate=self;
//webView.navigationDelegate=self;
// NSURL *url = [[NSBundle mainBundle] URLForResource:@"Untitled.html" withExtension:nil]; //从本地获取的URL
//NSURLRequest *request = [NSURLRequest requestWithURL:url];
//[webView loadRequest:request];

}


//==================webview 的代理方法=================
Sent before a web view begins loading a frame.请求发送前都会调用该方法,返回NO则不处理这个请求
//- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
//
//return YES;
//}
//
Sent after a web view starts loading a frame. 请求发送之后开始接收响应之前会调用这个方法
//-(void)webViewDidStartLoad:(UIWebView *)webView{
//
//}
//
Sent after a web view finishes loading a frame. 请求发送之后,并且服务器已经返回响应之后调用该方法
//-(void)webViewDidFinishLoad:(UIWebView *)webView{
NSString *str= [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.innerHTML"]; //获取整个网页的内容
NSLog(@"-sss--%@-",str);
NSString *str= [webView stringByEvaluatingJavaScriptFromString:@"document.getElementsByTagName('p')[0].innerHTML"]; //获取第一个P标签的内容
//NSString *str= [webView stringByEvaluatingJavaScriptFromString:@"myFunction(); "]; //通过执行JS代码获取第一个P标签的内容
//NSLog(@"-p标签--%@-",str);
//}
//
Sent if a web view failed to load a frame. 网页请求失败则会调用该方法
//- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
//
//}


/***=============WKWebView中的属性==========

// UIWebView 中会自动保存Cookie,如果登录了一次下次再次进入的时候,会记住登录状态
// 在WKWebView中,新增一个configuration属性,configuration 让WKWebView知道登录状态,
// configuration 可以通过已有的Cookie进行设置,也可以通过保存上一次的configuration进行设置
// WKWebViewConfiguration类中也有一些相应的属性
@property (nonatomic, readonly, copy) WKWebViewConfiguration *configuration;

// The methods of the WKNavigationDelegate protocol help you track the progress of the web site's main frame navigations and decide load policy for main frame and subframe navigations.
// WKWebView中,加入了网站导航的概念,这个对象决定主框架导航加载方法协议。
@property (nullable, nonatomic, weak) id navigationDelegate;

// The WKUIDelegate class provides methods for presenting native user interface
elements on behalf of a webpage.
// WKWebView中,加入了网站窗口的概念,这个对象决了webView窗口的一些方法协议。
@property (nullable, nonatomic, weak) id UIDelegate;

A WKBackForwardList object is a list of webpages previously visited in a web view that can be reached by going back or forward.
// WKWebView中,加入了网站列表的概念,这个WEBBackForwardList对象是以前在Web视图访问的网页,可以通过去后退或前进
@property (nonatomic, readonly, strong) WKBackForwardList *backForwardList;

*/


/***========MKWebview的对象方法========
// 这是加载网页最常用的一种方式,通过一个网页URL来加载一个WKWebView,这个URL可以是远程的也可以是本地的,例如我加载百度的主页
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;

// 根据一个文件,加载一个WKWebView
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL NS_AVAILABLE(10_11, 9_0);

// 这个方法需要将html文件读取为字符串从而加载为WKWebView,其中baseURL是我们自己设置的一个路径,用于寻找html文件中引用的图片等素材。
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;

// 这个方式使用的比较少,但也更加自由,其中data是文件数据,MIMEType是文件类型,characterEncodingName是编码类型,baseURL是素材资源路径
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL NS_AVAILABLE(10_11, 9_0);

*/

//- (nullable WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item;

/*! @abstract The page title.
@discussion @link WKWebView @/link is key-value observing (KVO) compliant
for this property.
*/
//@property (nullable, nonatomic, readonly, copy) NSString *title;

/*! @abstract The active URL.
@discussion This is the URL that should be reflected in the user
interface.
@link WKWebView @/link is key-value observing (KVO) compliant for this
property.
*/
//@property (nullable, nonatomic, readonly, copy) NSURL *URL;

/*! @abstract A Boolean value indicating whether the view is currently
loading content.
@discussion @link WKWebView @/link is key-value observing (KVO) compliant
for this property.
*/
//@property (nonatomic, readonly, getter=isLoading) BOOL loading;

/*! @abstract An estimate of what fraction of the current navigation has been completed.
@discussion This value ranges from 0.0 to 1.0 based on the total number of
bytes expected to be received, including the main document and all of its
potential subresources. After a navigation completes, the value remains at 1.0
until a new navigation starts, at which point it is reset to 0.0.
@link WKWebView @/link is key-value observing (KVO) compliant for this
property.
*/
//@property (nonatomic, readonly) double estimatedProgress;

/*! @abstract A Boolean value indicating whether all resources on the page
have been loaded over securely encrypted connections.
@discussion @link WKWebView @/link is key-value observing (KVO) compliant
for this property.
*/
//@property (nonatomic, readonly) BOOL hasOnlySecureContent;

/*! @abstract A SecTrustRef for the currently committed navigation.
@discussion @link WKWebView @/link is key-value observing (KVO) compliant
for this property.
*/
//@property (nonatomic, readonly, nullable) SecTrustRef serverTrust API_AVAILABLE(macosx(10.12), ios(10.0));

/*! @abstract A Boolean value indicating whether there is a back item in
the back-forward list that can be navigated to.
@discussion @link WKWebView @/link is key-value observing (KVO) compliant
for this property.
@seealso backForwardList.
*/
//@property (nonatomic, readonly) BOOL canGoBack;

/*! @abstract A Boolean value indicating whether there is a forward item in
the back-forward list that can be navigated to.
@discussion @link WKWebView @/link is key-value observing (KVO) compliant
for this property.
@seealso backForwardList.
*/
//@property (nonatomic, readonly) BOOL canGoForward;

/*! @abstract Navigates to the back item in the back-forward list.
@result A new navigation to the requested item, or nil if there is no back
item in the back-forward list.
*/
//- (nullable WKNavigation *)goBack;

/*! @abstract Navigates to the forward item in the back-forward list.
@result A new navigation to the requested item, or nil if there is no
forward item in the back-forward list.
*/
//- (nullable WKNavigation *)goForward;

/*! @abstract Reloads the current page.
@result A new navigation representing the reload.
*/
//- (nullable WKNavigation *)reload;

/*! @abstract Reloads the current page, performing end-to-end revalidation
using cache-validating conditionals if possible.
@result A new navigation representing the reload.
*/
//- (nullable WKNavigation *)reloadFromOrigin;

/*! @abstract Stops loading all resources on the current page.
*/
//- (void)stopLoading;

/* @abstract Evaluates the given JavaScript string.
@param javaScriptString The JavaScript string to evaluate.
@param completionHandler A block to invoke when script evaluation completes or fails.
@discussion The completionHandler is passed the result of the script evaluation or an error.
*/
//- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;

/*! @abstract A Boolean value indicating whether horizontal swipe gestures
will trigger back-forward list navigations.
@discussion The default value is NO.
*/
//@property (nonatomic) BOOL allowsBackForwardNavigationGestures;

/*! @abstract The custom user agent string or nil if no custom user agent string has been set.
*/
//@property (nullable, nonatomic, copy) NSString *customUserAgent API_AVAILABLE(macosx(10.11), ios(9.0));

/*! @abstract A Boolean value indicating whether link preview is allowed for any
links inside this WKWebView.
@discussion The default value is YES on Mac and iOS.
*/
//@property (nonatomic) BOOL allowsLinkPreview API_AVAILABLE(macosx(10.11), ios(9.0));

//#if TARGET_OS_IPHONE
/*! @abstract The scroll view associated with the web view.
*/
//@property (nonatomic, readonly, strong) UIScrollView *scrollView;
//#endif

//#if !TARGET_OS_IPHONE
/* @abstract A Boolean value indicating whether magnify gestures will
change the web view's magnification.
@discussion It is possible to set the magnification property even if
allowsMagnification is set to NO.
The default value is NO.
*/
//@property (nonatomic) BOOL allowsMagnification;

/* @abstract The factor by which the page content is currently scaled.
@discussion The default value is 1.0.
*/
//@property (nonatomic) CGFloat magnification;

/* @abstract Scales the page content by a specified factor and centers the
result on a specified point.
* @param magnification The factor by which to scale the content.
* @param point The point (in view space) on which to center magnification.
*/
//- (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point;


//==================MKWebview 的代理方法=================
//每当加载一个请求之前会调用该方法,通过该方法决定是否允许或取消请求的发送

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
//// 获得协议头(可以自定义协议头,根据协议头判断是否要执行跳转)
//NSString *scheme = navigationAction.request.URL.scheme;
//if ([scheme isEqualToString:@"zhixing"]) {
//// decisionHandler 对请求处理回调
////WKNavigationActionPolicyCancel:取消请求
////WKNavigationActionPolicyAllow:允许请求
//decisionHandler(WKNavigationActionPolicyCancel);
//return;
//}

NSLog(@"请求前会先进入这个方法webView:decidePolicyForNavigationActiondecisionHandler: %@\n\n",navigationAction.request);

decisionHandler(WKNavigationActionPolicyAllow);

}
//开始发送请求时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecifiedWKNavigation *)navigation
{
[UIApplicationsharedApplication].networkActivityIndicatorVisible =YES;
NSLog(@"webView:didStartProvisionalNavigation: 开始请求\n\n");
}

//返回响应前调用。并且已经能接收到响应,在收到响应后,决定是否跳转
//每当接收到服务器返回的数据时调用,通过该方法可以决定是否允许或取消导航
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{

NSLog(@"返回响应前先会调用这个方法 并且已经能接收到响应webView:decidePolicyForNavigationResponse:decisionHandler: Response?%@\n\n",navigationResponse.response);
//decisionHandler 对响应的处理
//WKNavigationActionPolicyCancel:取消响应
//WKNavigationActionPolicyAllow:允许响应

decisionHandler(WKNavigationResponsePolicyAllow);
}

//响应的内容到达主页面的时候响应,刚准备开始渲染页面应用,页面开始返回
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
NSLog(@"webView:didCommitNavigation:响应的内容到达主页面的时候响应,刚准备开始渲染页面应用 \n\n");
}

//响应渲染完成后调用该方法,页面加载完成
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecifiedWKNavigation *)navigation
{
[UIApplicationsharedApplication].networkActivityIndicatorVisible =NO;
NSLog(@"webView:didFinishNavigation: 响应渲染完成后调用该方法webView : %@-- navigation : %@\n\n",webView,navigation);

[webView evaluateJavaScript:[NSStringstringWithFormat:@"myFunction(); "]completionHandler:^(id_Nullable response,NSError *_Nullable error) {
NSLog(@"-response-%@-",response); //response就是请求的结果;

}]; //从加载过来的网页html文件中的JS

}


//当Web视图的网页内容被终止时调用。
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{
NSLog(@"webViewWebContentProcessDidTerminate: 当Web视图的网页内容被终止时调用。");
}


// 启动时加载数据发生错误就会调用这个方法
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
// 类似 UIWebView的- webView:didFailLoadWithError:

NSLog(@"webView:didFailProvisionalNavigation:withError:启动时加载数据发生错误就会调用这个方法。\n\n");
}


//当一个正在提交的页面在跳转过程中出现错误时调用这个方法
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{
NSLog(@"webView:didFailNavigation:当一个正在提交的页面在跳转过程中出现错误时调用这个方法。\n\n");
}



//接收到服务器跳转请求之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{

NSLog(@"webView:didReceiveServerRedirectForProvisionalNavigation:重定向的时候就会调用\n\n");
}

/**
*当收到服务器返回的受保护空间(证书)时调用
*@param challenge安全质询-->包含受保护空间和证书
*@param completionHandler 完成回调-->告诉服务器如何处置证书
*/
- (void)webView:(WKWebView*)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,NSURLCredential *__nullablecredential))completionHandler {
// 创建凭据对象
NSURLCredential *credential = [NSURLCredentialcredentialForTrust:challenge.protectionSpace.serverTrust];
// 告诉服务器信任证书
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
}

//WKScriptMessageHandler
//这个协议中包含一个必须实现的方法,这个方法是提高App与web端交互的关键,它可以直接将接收到的JS脚本转化为OC或Swift对象。(当然,在UIWebView也可以通过“曲线救国”的方式与web进行交互,著名的Cordova框架就是这种机制)
从web界面中接收到一个脚本时调用
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{//方法中主要是收到JS的回执脚本就会运行一次
NSLog(@"可以和js交互");
////可以在这个方法里面进行app和js的交互
}
//

/*
WKUIDelegate
以下三个代理方法全都是与界面弹出提示框相关的,针对web界面的三种提示框(警告框,提示框,输入框)分别对应三种代理方法
*/
//为了响应html的alert和confirm事件,需要添加WKWebView的如下代理方法,
//提示框,如果JS中有提示框alert(),则必须实现下面的代理方法,否则不会弹出提示框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
NSLog(@"%s",__FUNCTION__);
UIAlertController *alert = [UIAlertControlleralertControllerWithTitle:@"alert"message:@"JS调用alert"preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertActionactionWithTitle:@"确定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction *_Nonnull action) {
completionHandler(); //这个必须执行,否则会崩溃
}]];

[selfpresentViewController:alertanimated:YEScompletion:NULL];
NSLog(@"%@", message);
}

//选择框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {
NSLog(@"%s",__FUNCTION__);

UIAlertController *alert = [UIAlertControlleralertControllerWithTitle:@"confirm"message:@"JS调用confirm"preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertActionactionWithTitle:@"确定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction *_Nonnull action) {
completionHandler(YES); //这个必须执行,否则会崩溃
}]];
[alert addAction:[UIAlertActionactionWithTitle:@"取消"style:UIAlertActionStyleCancelhandler:^(UIAlertAction *_Nonnull action) {
completionHandler(NO); //这个必须执行,否则会崩溃
}]];
[selfpresentViewController:alertanimated:YEScompletion:NULL];

NSLog(@"%@", message);
}

//输入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullableNSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *__nullable result))completionHandler {
NSLog(@"%s",__FUNCTION__);

NSLog(@"%@", prompt);
UIAlertController *alert = [UIAlertControlleralertControllerWithTitle:@"textinput"message:@"JS调用输入框"preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField *_Nonnull textField) {
textField.textColor = [UIColorredColor];
}];

[alert addAction:[UIAlertActionactionWithTitle:@"确定"style:UIAlertActionStyleDefaulthandler:^(UIAlertAction *_Nonnull action) {
completionHandler([[alert.textFieldslastObject]text]); //这个必须执行,否则会崩溃
}]];

[selfpresentViewController:alertanimated:YEScompletion:NULL];
}

- (void)didReceiveMemoryWarning {
【WKWebView 和UIWebView、网页缓存、网路请求缓存】[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}


@end

======================截获js中的URL,然后调用OC方法实现

使用WKWebView的时候,如果想要实现JS调用OC方法,除了拦截URL之外,还有一种简单的方式。那就是利用WKWebView的新特性MessageHandler来实现JS调用原生方法。

MessageHandler 是什么?

WKWebView 初始化时,有一个参数叫configuration,它是WKWebViewConfiguration类型的参数,而WKWebViewConfiguration有一个属性叫userContentController,它又是WKUserContentController类型的参数。WKUserContentController对象有一个方法- addScriptMessageHandler:name:,我把这个功能简称为MessageHandler。

- addScriptMessageHandler:name:有两个参数,第一个参数是userContentController的代理对象,第二个参数是JS里发送postMessage的对象。
所以要使用MessageHandler功能,就必须要实现WKScriptMessageHandler协议。
我们在该API的描述里可以看到在JS中的使用方法:

window.webkit.messageHandlers..postMessage()
//其中,就是上面方法里的第二个参数`name`。
//例如我们调用API的时候第二个参数填@"Share",那么在JS里就是:
//window.webkit.messageHandlers.Share.postMessage()
//是一个键值对,键是body,值可以有多种类型的参数。
// 在`WKScriptMessageHandler`协议中,我们可以看到mssage是`WKScriptMessage`类型,有一个属性叫body。
// 而注释里写明了body的类型:
Allowed types are NSNumber, NSString, NSDate, NSArray, NSDictionary, and NSNull.
怎么使用MessageHandler?

1.创建WKWebViewConfiguration对象,配置各个API对应的MessageHandler。

WKUserContentController对象可以添加多个scriptMessageHandler。
看了示例代码,会很容易理解。示例代码如下:

WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.userContentController = [WKUserContentController new];

[configuration.userContentController addScriptMessageHandler:self name:@"ScanAction"];
[configuration.userContentController addScriptMessageHandler:self name:@"Location"];
[configuration.userContentController addScriptMessageHandler:self name:@"Share"];
[configuration.userContentController addScriptMessageHandler:self name:@"Color"];
[configuration.userContentController addScriptMessageHandler:self name:@"Pay"];
[configuration.userContentController addScriptMessageHandler:self name:@"Shake"];
[configuration.userContentController addScriptMessageHandler:self name:@"GoBack"];
[configuration.userContentController addScriptMessageHandler:self name:@"PlaySound"];

WKPreferences *preferences = [WKPreferencesnew];
preferences.javaScriptCanOpenWindowsAutomatically = YES;
preferences.minimumFontSize = 40.0;
configuration.preferences = preferences;

2.创建WKWebView。

这里没什么好说的,直接看示例代码吧:

self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];

NSString *urlStr = [[NSBundlemainBundle]pathForResource:@"index.html"ofType:nil];
NSURL *fileURL = [NSURLfileURLWithPath:urlStr];
[self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL];

self.webView.navigationDelegate =self;
self.webView.UIDelegate = self;
[self.view addSubview:self.webView];
3.实现协议方法。

我这里实现了两个协议,WKUIDelegate是因为我在JS中弹出了alert。WKScriptMessageHandler是因为我们要处理JS调用OC方法的请求。
先看实现协议方法的示例代码:

#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
//message.body--Allowed types are NSNumber, NSString, NSDate, NSArray,NSDictionary, and NSNull.
if ([message.name isEqualToString:@"ScanAction"]) {
NSLog(@"扫一扫");
} elseif ([message.name isEqualToString:@"Location"]) {
[self getLocation];
} elseif ([message.name isEqualToString:@"Share"]) {
[self shareWithParams:message.body];
} elseif ([message.name isEqualToString:@"Color"]) {
[self changeBGColor:message.body];
} elseif ([message.name isEqualToString:@"Pay"]) {
[self payWithParams:message.body];
} elseif ([message.name isEqualToString:@"Shake"]) {
[self shakeAction];
} elseif ([message.name isEqualToString:@"GoBack"]) {
[self goBack];
} elseif ([message.name isEqualToString:@"PlaySound"]) {
[self playSound:message.body];
}
}
WKScriptMessage有两个关键属性name和 body。
因为我们给每一个OC方法取了一个name,那么我们就可以根据name来区分执行不同的方法。body中存着JS要给OC传的参数。
关于参数body的解析,我就举一个body中放字典的例子,其他的稍后可以看demo。
解析JS 调用OC 实现分享的参数:

- (void)shareWithParams:(NSDictionary *)tempDic
{
if (![tempDic isKindOfClass:[NSDictionary class]]) {
return;
}

NSString *title = [tempDic objectForKey:@"title"];
NSString *content = [tempDic objectForKey:@"content"];
NSString *url = [tempDic objectForKey:@"url"];
// 在这里执行分享的操作

// 将分享结果返回给js
NSString *jsStr = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url];
[self.webView evaluateJavaScript:jsStr completionHandler:^(id_Nullable result, NSError *_Nullable error) {
NSLog(@"%@----%@",result, error);
}];
}
message.boby 就是JS里传过来的参数。我们不同的方法先做一下容错性判断。然后正常取值就可以了。

4.处理HTML中JS调用。

HMTL的源码跟之前的HTML内容差不多,只有JS的调用部分改变了。

// 传null
function scanClick() {
window.webkit.messageHandlers.ScanAction.postMessage(null);
}
// 传字典
function shareClick() {
window.webkit.messageHandlers.Share.postMessage({title:'测试分享的标题',content:'测试分享的内容',url:'http://www.baidu.com'});
}
// 传字符串
function playSound() {
window.webkit.messageHandlers.PlaySound.postMessage('shake_sound_male.wav');
}
// 传数组
function colorClick() {
window.webkit.messageHandlers.Color.postMessage([67,205,128,0.5]);
}

==========读取cookie

开发过程中,使用UIWebView加载电脑版网页,登录出错,
请求头有误,webView请求头为:

User-Agent: Mozilla/5.0 (iPad; CPU OS 7_0_2 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/11A501

safari浏览器请求头为:

User-Agent: Mozilla/5.0 (iPad; CPU OS 7_0_2 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11A501 Safari/9537.53
解决办法:
在webView加载之前执行下面代码

NSString *userAgent = [_webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
userAgent = [userAgent stringByAppendingString:@" Version/7.0 Safari/9537.53"];
[[NSUserDefaults standardUserDefaults] registerDefaults:@{@"UserAgent": userAgent}];

读取cookie方法:


NSHTTPCookieStorage *sharedHTTPCookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookies = [sharedHTTPCookieStorage cookiesForURL:request.URL];
NSEnumerator *enumerator = [cookies objectEnumerator];
NSHTTPCookie *cookie;
while (cookie = [enumerator nextObject]) {
NSLog(@"COOKIE{name: %@, value: %@}", [cookie name], [cookie value]);
}


=========WKNavigationDelegate====
@protocol WKNavigationDelegate

@optional
//请求之前,决定是否要跳转:用户点击网页上的链接,需要打开新页面时,将先调用这个方法。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
//接收到相应数据后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;



- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{

//WKNavigationActionPolicyCancel,
//WKNavigationActionPolicyAllow,
//navigationAction.sourceFrame;
//navigationAction.targetFrame;
//navigationAction.request;
if (navigationAction.navigationType == WKNavigationTypeLinkActivated){
decisionHandler(WKNavigationActionPolicyCancel);
}else{
decisionHandler(WKNavigationActionPolicyAllow);
}
}
//接收到相应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
if (!navigationResponse.isForMainFrame){
decisionHandler(WKNavigationResponsePolicyCancel);
}else{
decisionHandler(WKNavigationResponsePolicyAllow);
}
NSLog(@"2");

}
}


//页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecifiedWKNavigation *)navigation;
// 主机地址被重定向时调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecifiedWKNavigation *)navigation;
//页面加载失败时调用---比如断网的时候
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecifiedWKNavigation *)navigation withError:(NSError *)error;
// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecifiedWKNavigation *)navigation;
// 页面加载完毕时调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecifiedWKNavigation *)navigation;
//跳转失败时调用
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecifiedWKNavigation *)navigation withError:(NSError *)error;
// 如果需要证书验证,与使用AFN进行HTTPS证书验证是一样的
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,NSURLCredential *__nullable credential))completionHandler;
//9.0才能使用,web内容处理中断时会触发
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webViewNS_AVAILABLE(10_11, 9_0);

    推荐阅读