【转】iOS WKWebView基本使用总结

UIWebView废弃,迁移WKWebView

WWDC 2018中 ,在安全方面,Session上来就宣布了一件重量级的大事,UIWebView正式被官方宣布废弃,建议开发者迁移适配到WKWebView。

在XCode9中UIWebView还是 NS_CLASS_AVAILABLE_IOS(2_0),而我们从最新的Xcode10再看UIWebView就已经是这个样子了

UIKIT_EXTERN API_DEPRECATED("No longer supported; please adopt WKWebView.", ios(2.0, 12.0)) API_PROHIBITED(tvos, macos) @interface UIWebView : UIView <NSCoding, UIScrollViewDelegate>

WKWebView从诞生之初相比UIWebView有太多的优势,无论是内存泄露还是网页性能,并且WKWebView可以同时支持macOS与iOS。由于WKWebView的独特设计,网页运行在独立的进程,如果网页遇到Crash,不会影响App的正常运行。

但是WKWebView不支持JSContext,不支持NSURLProtocol,Cookie管理蛋疼等问题确实给让不少开发者不想丢弃UIWebView,但最后通牒来了还是准备着手替换吧。

下面的一些方法均是看了许多大神的博客后总结到一起的,自己并未一一验证,后续发现错误会去纠正。

WKWebView的特点

  • 性能高,稳定性好,占用的内存比较小,
  • 支持JS交互
  • 支持HTML5 新特性
  • 可以添加进度条(然并卵,不好用,还是习惯第三方的)。
  • 支持内建手势,
  • 据说高达60fps的刷新频率(不卡)

初始化WKWebView

一、先导入头文件 #import <WebKit/WebKit.h>

二、WKWebView创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.selectionGranularity = WKSelectionGranularityDynamic;
config.allowsInlineMediaPlayback = YES;
WKPreferences *preferences = [WKPreferences new];
//是否支持JavaScript
preferences.javaScriptEnabled = YES;
//不通过用户交互,是否可以打开窗口
preferences.javaScriptCanOpenWindowsAutomatically = YES;
config.preferences = preferences;
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:config];
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com/"]]];
self.webView.navigationDelegate = self;
self.webView.UIDelegate = self;
//开了支持滑动返回
self.webView.allowsBackForwardNavigationGestures = YES;
[self.view addSubview:self.webView];

[self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
[self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];
  • WKWebViewConfiguration 用于配置WKWebView的一些属性
  • WKPreferences 用于配置WKWebView视图的一些属性
  • 加上<WKNavigationDelegate, WKUIDelegate>两个代理

三、WKNavigationDelegate代理事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
NSLog(@"页面开始加载时调用");
}
// 当内容开始返回时调用 内容开始到达主帧时被调用(即将完成)
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
NSLog(@"当内容开始返回时调用");
}
// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {//这里修改导航栏的标题,动态改变
self.title = webView.title;
NSLog(@"页面加载完成之后调用");
}

// 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

// NSLog(@"webView==%@",webView);
// NSLog(@"navigationResponse==%@",navigationResponse);

WKNavigationResponsePolicy actionPolicy = WKNavigationResponsePolicyAllow;
//这句是必须加上的,不然会异常
decisionHandler(actionPolicy);

}
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{

self.title = webView.title;

WKNavigationActionPolicy actionPolicy = WKNavigationActionPolicyAllow;

if (navigationAction.navigationType == WKNavigationTypeBackForward) {//判断是返回类型

//同时设置返回按钮和关闭按钮为导航栏左边的按钮 这里可以监听左滑返回事件,仿微信添加关闭按钮。
// self.navigationItem.leftBarButtonItems = @[self.backBtn, self.closeBtn];

//可以在这里找到指定的历史页面做跳转
// if (webView.backForwardList.backList.count>0) { //得到栈里面的list
// DLog(@"%@",webView.backForwardList.backList);
// DLog(@"%@",webView.backForwardList.currentItem);
// WKBackForwardListItem * item = webView.backForwardList.currentItem; //得到现在加载的list
// for (WKBackForwardListItem * backItem in webView.backForwardList.backList) { //循环遍历,得到你想退出到
// //添加判断条件
// [webView goToBackForwardListItem:[webView.backForwardList.backList firstObject]];
// }
// }
}

NSLog(@"webView.backForwardList.backList.count==%lu",(unsigned long)webView.backForwardList.backList.count);

if (webView.backForwardList.backList.count > 0) {
self.navigationItem.leftBarButtonItems = @[self.backBtn, self.closeBtn];
}else {
self.navigationItem.leftBarButtonItems = nil;
}

//这句是必须加上的,不然会异常
decisionHandler(actionPolicy);
}

//MARK: 以下为不常用的

// 接收到服务器跳转请求之后再执行
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@"接收到服务器跳转请求之后再执行");
}

// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation {
NSLog(@"页面加载失败时调用");
}

//在提交的主帧中发生错误时调用
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
NSLog(@"在提交的主帧中发生错误时调用");
}

//当webView需要响应身份验证时调用(如需验证服务器证书)
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
// NSLog(@"当webView需要响应身份验证时调用(如需验证服务器证书)");
// completionHandler(nil,nil);
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {

NSURLCredential *card = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust];

completionHandler(NSURLSessionAuthChallengeUseCredential,card);

}
}

//当webView的web内容进程被终止时调用。(iOS 9.0之后)
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)) {
NSLog(@"当webView的web内容进程被终止时调用。(iOS 9.0之后)");
}

四、WKUIDelegate代理事件,主要实现与js的交互

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

//显示一个JS的Alert(与JS交互) 在JS端调用alert函数时,会触发此代理方法
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {

NSLog(@"弹窗alert====message==%@==frame==%@",message,frame);

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *a2 = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}];
[alert addAction:a2];

[self presentViewController:alert animated:YES completion:nil];

// completionHandler();

}

//弹出一个输入框(与JS交互的)JS端调用prompt函数时,会触发此方法
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler {

NSLog(@"弹窗输入框==prompt==%@==defaultText==%@==frame==%@",prompt,defaultText,frame);

UIAlertController *alert = [UIAlertController alertControllerWithTitle:prompt message:defaultText preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *a1 = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
//这里必须执行不然页面会加载不出来
completionHandler(@"");
}];
UIAlertAction *a2 = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSLog(@"%@",[alert.textFields firstObject].text);
completionHandler([alert.textFields firstObject].text);
}];
[alert addAction:a1];
[alert addAction:a2];
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
NSLog(@"textField.text==%@",textField.text);
}];
[self presentViewController:alert animated:YES completion:nil];

}

//显示一个确认框(JS的) JS端调用confirm函数时,会触发此方法
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {

NSLog(@"弹窗确认框==message==%@==frame==%@",message,frame);

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message message:nil preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler(NO);
}];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDestructive handler:^(UIAlertAction * _Nonnull action) {
completionHandler(YES);
}];
[alertController addAction:cancelAction];
[alertController addAction:okAction];
[self presentViewController:alertController animated:YES completion:nil];

}

五、JS调用OC方法

  • 在JS中调用方法为:

window.webkit.messageHandlers.方法名.postMessage(参数);

  • 在OC中:
1
2
3
4
5
6

//设置addScriptMessageHandler与name.并且设置<WKScriptMessageHandler>协议与协议方法
[[_webView configuration].userContentController addScriptMessageHandler:self name:@"takePicturesByNative"];

//在dealloc方法中需要释放掉
[[_webView configuration].userContentController removeScriptMessageHandlerForName:@"takePicturesByNative"];
  • 在WKScriptMessageHandler中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
if ([message.name isEqualToString:@"takePicturesByNative"]) {
[self takePicturesByNativeWithBody:message.body];
}
}
- (void)takePicturesByNativeWithBody:(NSString *)body {
NSLog(@"调用了takePicturesByNative方法");

UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:body preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *a1 = [UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {

}];
[alert addAction:a1];

[self presentViewController:alert animated:YES completion:nil];

}

在使用上述方法中发现,addScriptMessageHandler:self中发生了循环引用,造成webview不会被释放掉,故经测试有以下两种解决方案:

1.新建个WeakScriptMessageDelegate类

1
2
3
4
5
6
7
8
9
10
11

#import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>

@interface WeakScriptMessageDelegate : NSObject <WKScriptMessageHandler>

@property (nonatomic, assign) id<WKScriptMessageHandler> scriptDelegate;

+ (instancetype)scriptWithDelegate:(id<WKScriptMessageHandler>)delegate;

@end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

@implementation WeakScriptMessageDelegate

- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate {
self = [super init];
if (self) {
_scriptDelegate = scriptDelegate;
}
return self;
}

+ (instancetype)scriptWithDelegate:(id<WKScriptMessageHandler>)delegate {
return [[WeakScriptMessageDelegate alloc]initWithDelegate:delegate];
}

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
[self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
}

@end
设置addScriptMessageHandler方法更换为:
1
2

[[_webView configuration].userContentController addScriptMessageHandler:[WeakScriptMessageDelegate scriptWithDelegate:self] name:@"takePicturesByNative"];

2.不在初始化时添加ScriptMessageHandler, 而是和Notificenter/KVC一个思路

1
2
3
4
5
6
7
8
9
10
11
12
13

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];

[_webView.configuration.userContentController addScriptMessageHandler:self name:@"takePicturesByNative"];
}

- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];

[_webView.configuration.userContentController removeScriptMessageHandlerForName:@"takePicturesByNative"];

}

六、OC调用JS方法

1
2
3
4
5
6
7
8
9
10

//设置JS
// NSString *inputValueJS = @"document.getElementsByName(‘input‘)[0].attributes[‘value‘].value";
// NSString *inputValueJS = @"shareCallback()";
NSString *inputValueJS = @"js代码";

//执行JS
[webView evaluateJavaScript:inputValueJS completionHandler:^(id _Nullable response, NSError * _Nullable error) {
NSLog(@"value: %@ error: %@", response, error);
}];

七、给webview添加请求头

1
2
3
4
5
6

NSString *urlString = @"https://www.baidu.com/";
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request addValue:@"123" forHTTPHeaderField:@"token"];
[self.webView loadRequest:request];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{

WKNavigationActionPolicy actionPolicy = WKNavigationActionPolicyAllow;

//同步HTTPHeaderFields里的参数

NSMutableURLRequest *mutableRequest = [navigationAction.request mutableCopy];
NSDictionary *requestHeaders = navigationAction.request.allHTTPHeaderFields;
//我们项目使用的token同步的,cookie的话类似
if (requestHeaders[@"token"]) {
decisionHandler(actionPolicy);//允许跳转
}else {
//这里添加请求头,把需要的都添加进来
[mutableRequest setValue:@"123" forHTTPHeaderField:@"token"];

[webView loadRequest:mutableRequest];
decisionHandler(actionPolicy);//允许跳转
}

}

注:在UIWeb里边是直接用的request 但是在这里需要写上navigationAction.出来的request

八、WKWebView加载不受信任的https

解决方法:在plist文件中设置Allow Arbitrary Loads in Web Content 置为 YES,并实现wkwebView下面的代理方法,就可解决

1
2
3
4
5
6
7
8
9
10
11

- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{

if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {

NSURLCredential *card = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust];

completionHandler(NSURLSessionAuthChallengeUseCredential,card);

}
}

九、监听WKWebView的进度条和标题

1
2
3
4
5
6
7

[self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
[self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];

//需要注意的是销毁的时候一定要移除监控
[self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
[self.webView removeObserver:self forKeyPath:@"title"];
1
2
3
4
5
6
7
8
9
10
11
12
13

@property (nonatomic, weak) CALayer *progressLayer;

UIView *progress = [[UIView alloc]init];
progress.frame = CGRectMake(0, 0, KScreenWidth, 3);
progress.backgroundColor = [UIColor clearColor];
[self.view addSubview:progress];

CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(0, 0, 0, 3);
layer.backgroundColor = [UIColor greenColor].CGColor;
[progress.layer addSublayer:layer];
self.progressLayer = layer;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

#pragma mark - KVO回馈

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{

if ([keyPath isEqualToString:@"estimatedProgress"]) {
self.progressLayer.opacity = 1;
if ([change[@"new"] floatValue] <[change[@"old"] floatValue]) {
return;
}
self.progressLayer.frame = CGRectMake(0, 0, KScreenWidth*[change[@"new"] floatValue], 3);
if ([change[@"new"]floatValue] == 1.0) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
self.progressLayer.opacity = 0;
self.progressLayer.frame = CGRectMake(0, 0, 0, 3);
});
}
}else if ([keyPath isEqualToString:@"title"]){
self.title = change[@"new"];
}

}

十、解决cookie问题

以前UIWebView会自动去NSHTTPCookieStorage中读取cookie,但是WKWebView并不会去读取,因此导致cookie丢失以及一系列问题,解决方式就是在request中手动帮其添加上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

self.webView.UIDelegate = self;
self.webView.navigationDelegate = self;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.test.com"]];
[request addValue:[self readCurrentCookieWithDomain:@"http://www.test.com/"] forHTTPHeaderField:@"Cookie"];
[self.webView loadRequest:request];

- (NSString *)readCurrentCookieWithDomain:(NSString *)domainStr{
NSHTTPCookieStorage*cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSMutableString * cookieString = [[NSMutableString alloc]init];
for (NSHTTPCookie*cookie in [cookieJar cookies]) {
[cookieString appendFormat:@"%@=%@;",cookie.name,cookie.value];
}

//删除最后一个“;”
[cookieString deleteCharactersInRange:NSMakeRange(cookieString.length - 1, 1)];
return cookieString;
}

但是这只能解决第一次进入的cookie问题,如果页面内跳转(a标签等)还是取不到cookie,因此还要再加代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {

//取出cookie
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
//js函数
NSString *JSFuncString =
@"function setCookie(name,value,expires)\
{\
var oDate=new Date();\
oDate.setDate(oDate.getDate()+expires);\
document.cookie=name+‘=‘+value+‘;expires=‘+oDate+‘;path=/‘\
}\
function getCookie(name)\
{\
var arr = document.cookie.match(new RegExp(‘(^| )‘+name+‘=([^;]*)(;|$)‘));\
if(arr != null) return unescape(arr[2]); return null;\
}\
function delCookie(name)\
{\
var exp = new Date();\
exp.setTime(exp.getTime() - 1);\
var cval=getCookie(name);\
if(cval!=null) document.cookie= name + ‘=‘+cval+‘;expires=‘+exp.toGMTString();\
}";

//拼凑js字符串
NSMutableString *JSCookieString = JSFuncString.mutableCopy;
for (NSHTTPCookie *cookie in cookieStorage.cookies) {
NSString *excuteJSString = [NSString stringWithFormat:@"setCookie(‘%@‘, ‘%@‘, 1);", cookie.name, cookie.value];
[JSCookieString appendString:excuteJSString];
}
//执行js
[webView evaluateJavaScript:JSCookieString completionHandler:nil];

}

十一、加载页面后自动关闭的问题

问题描述,我加载一web页面后,进行各种操作,比说我充值,什么的,然后想要在充值提出成功后自顶关闭这个web页面回到上一层或者返回到某一个界面,就用下面的方法,一般判断URL 包含的字符串都是后台给定的,在这里只需要判断就好了!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

//**WKNavigationDelegate**里面的代理方法(上面有)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
//获取请求的url路径.
NSString *requestString = navigationResponse.response.URL.absoluteString;
WKLog(@"requestString:%@",requestString);
// 遇到要做出改变的字符串
NSString *subStr = @"www.baidu.com";
if ([requestString rangeOfString:subStr].location != NSNotFound) {
WKLog(@"这个字符串中有subStr");
//回调的URL中如果含有百度,就直接返回,也就是关闭了webView界面
[self.navigationController popViewControllerAnimated:YES];
}

decisionHandler(WKNavigationResponsePolicyAllow);

}

十二、清除缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//清除本地缓存
- (void)clearCache {
/* 取得Library文件夹的位置*/
NSString *libraryDir = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,NSUserDomainMask, YES)[0];
/* 取得bundle id,用作文件拼接用*/ NSString *bundleId = [[[NSBundle mainBundle] infoDictionary]objectForKey:@"CFBundleIdentifier"];
/* * 拼接缓存地址,具体目录为App/Library/Caches/你的APPBundleID/fsCachedData */
NSString *webKitFolderInCachesfs = [NSString stringWithFormat:@"%@/Caches/%@/fsCachedData",libraryDir,bundleId];
NSError *error;
/* 取得目录下所有的文件,取得文件数组*/
NSFileManager *fileManager = [NSFileManager defaultManager];
//NSArray *fileList = [[NSArray alloc] init];
//fileList便是包含有该文件夹下所有文件的文件名及文件夹名的数组
NSArray *fileList = [fileManager contentsOfDirectoryAtPath:webKitFolderInCachesfs error:&error];
/* 遍历文件组成的数组*/
for(NSString * fileName in fileList)
{
/* 定位每个文件的位置*/
NSString * path = [[NSBundle bundleWithPath:webKitFolderInCachesfs] pathForResource:fileName ofType:@""];
/* 将文件转换为NSData类型的数据*/
NSData * fileData = [NSData dataWithContentsOfFile:path];
/* 如果FileData的长度大于2,说明FileData不为空*/
if(fileData.length >2)
{
/* 创建两个用于显示文件类型的变量*/
int char1 =0;
int char2 =0;
[fileData getBytes:&char1 range:NSMakeRange(0,1)];
[fileData getBytes:&char2 range:NSMakeRange(1,1)];
/* 拼接两个变量*/ NSString *numStr = [NSString stringWithFormat:@"%i%i",char1,char2];
/* 如果该文件前四个字符是6033,说明是Html文件,删除掉本地的缓存*/
if([numStr isEqualToString:@"6033"])
{
[[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:@"%@/%@",webKitFolderInCachesfs,fileName]error:&error]; continue;

}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

- (void)cleanCacheAndCookie {
//清除cookies
NSHTTPCookie *cookie;
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (cookie in [storage cookies])
{
[storage deleteCookie:cookie];
}

[[NSURLCache sharedURLCache] removeAllCachedResponses];
NSURLCache * cache = [NSURLCache sharedURLCache];
[cache removeAllCachedResponses];
[cache setDiskCapacity:0];
[cache setMemoryCapacity:0];

if (@available(iOS 9.0, *)) {

WKWebsiteDataStore *dateStore = [WKWebsiteDataStore defaultDataStore];
[dateStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes]
completionHandler:^(NSArray<WKWebsiteDataRecord *> * __nonnull records)
{
for (WKWebsiteDataRecord *record in records)
{

[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:record.dataTypes
forDataRecords:@[record]
completionHandler:^
{
NSLog(@"Cookies for %@ deleted successfully",record.displayName);
}];
}
}];

}

}
1
2
3
4
5
6
7
8
9

- (void)dealloc {

[_webView stopLoading];
[_webView setNavigationDelegate:nil];
[self clearCache];
[self cleanCacheAndCookie];

}

附:demo中使用的返回上一页和关闭浏览器的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

#pragma mark - Actions

- (void)backNative {
//判断是否有上一层H5页面
if ([self.webView canGoBack]) {
//如果有则返回
[self.webView goBack];
//同时设置返回按钮和关闭按钮为导航栏左边的按钮
// self.navigationItem.leftBarButtonItems = @[self.backBtn, self.closeBtn];
}else {
[self closeNative];
}
}

- (void)closeNative {

if (self.webView.backForwardList.backList.count>0) { //得到栈里面的list
NSLog(@"backList==%@",self.webView.backForwardList.backList);
NSLog(@"currentItem==%@",self.webView.backForwardList.currentItem);

[self.webView goToBackForwardListItem:[self.webView.backForwardList.backList firstObject]];

// WKBackForwardListItem * item = self.webView.backForwardList.currentItem; //得到现在加载的list
// for (WKBackForwardListItem * backItem in self.webView.backForwardList.backList) { //循环遍历,得到你想退出到
// //添加判断条件
// [self.webView goToBackForwardListItem:[self.webView.backForwardList.backList firstObject]];
// }
}

[self.navigationController popViewControllerAnimated:YES];
}

【转】iOS WKWebView基本使用总结

上一篇:IOS 发布 程序截图问题


下一篇:C#实现GDI+基本图的缩放、拖拽、移动