今天试了试WKWebView,试出来很多问题.记录如下
初始化方法如下:
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
4、WKWebView的一些简单属性的介绍
backForwardList 浏览历史
title 网页标题 支持KVO
URL 正在显示的URL 支持KVO
loading 是否正在加载 支持KVO
estimatedProgress 加载进度 支持KVO
canGoBack canGoForward 能否后退 前进 支持KVO
reload 重新加载
stopLoading 停止加载
allowsBackForwardNavigationGestures 是否允许侧滑返回上一页
5、WKWebView的加载方法跟UIWebView基本一样
- loadRequest:
- loadHTMLString:
- loadData:
WKWebViewUIDelegate 代理方法
//当需要打开一个新窗口的时候的调用,如a标签的target='_blank',需要返回一个新的Webview
- (nullable WKWebView )webView:(WKWebView )webView createWebViewWithConfiguration:(WKWebViewConfiguration )configuration forNavigationAction:(WKNavigationAction )navigationAction windowFeatures:(WKWindowFeatures )windowFeatures
以下三个类似alert的代理,一定要调用completionHandler(),这个回调,告诉webview结果
//webview上需要弹出alert的时候调用此方法,如果不实现此方法,则webview的alert是显示不出来,alert类似于只有确定按钮的UIAlertView
- (void)webView:(WKWebView )webView runJavaScriptAlertPanelWithMessage:(NSString )message initiatedByFrame:(WKFrameInfo )frame completionHandler:(void (^)(void))completionHandler
//webview上需要弹出confirm的时候调用此方法,如果不实现此方法,则webview的confirm是显示不出来,confirm类似于有确定按钮和取消按钮的UIAlertView
- (void)webView:(WKWebView )webView runJavaScriptConfirmPanelWithMessage:(NSString )message initiatedByFrame:(WKFrameInfo )frame completionHandler:(void (^)(BOOL result))completionHandler
// webview上需要弹出prompt的时候调用此方法,如果不实现此方法,则webview的prompt是显示不出来,prompt类似于带一个textField的UIAlertView。defaultText相当于textField的placeholed
- (void)webView:(WKWebView )webView runJavaScriptTextInputPanelWithPrompt:(NSString )prompt defaultText:(nullable NSString )defaultText initiatedByFrame:(WKFrameInfo )frame completionHandler:(void (^)(NSString __nullable result))completionHandler
//window.close() 的时候调用
- (void)webViewDidClose:(WKWebView *)webView
WKWebViewNavigationDelegate代理方法
//决定是否允许发起这个请求
- (void)webView:(WKWebView )webView decidePolicyForNavigationAction:(WKNavigationAction )navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
//在webview有响应之后,再次决定是否允许这个请求
- (void)webView:(WKWebView )webView decidePolicyForNavigationResponse:(WKNavigationResponse )navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandle
//webview开始加载的时候调用
- (void)webView:(WKWebView )webView didStartProvisionalNavigation:(null_unspecified WKNavigation )navigation
//webview内容已经加载结束,但是上面的某些资源比如图片加载之前调用
- (void)webView:(WKWebView )webView didCommitNavigation:(null_unspecified WKNavigation )navigation
//webview加载结束的时候调用
- (void)webView:(WKWebView )webView didCommitNavigation:(null_unspecified WKNavigation )navigation
//webview加载失败的时候调用
- (void)webView:(WKWebView )webView didFailProvisionalNavigation:(null_unspecified WKNavigation )navigation withError:(NSError )error
//webview在commit过程中失败的时候调用,例如在didCommitNavigation这个代理方法中调用webview的stopLoading方法
- (void)webView:(WKWebView )webView didFailNavigation:(null_unspecified WKNavigation )navigation withError:(NSError )error
WKScriptMessageHandler
//收到JavaScript回调的时候调用
- (void)userContentController:(WKUserContentController )userContentController didReceiveScriptMessage:(WKScriptMessage )message
WKWebViewConfiguration里面有个userContentController。可通过它为webview注入javaScript代码。并且可以添加监听javaScript的回调。
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
WKUserScript *userScript = [[WKUserScript alloc]initWithSource:@"js代码" injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[configuration.userContentController addUserScript:userScript];
OC执行JS代码:调用JS代码
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler;
//completionHandler 拥有两个参数,一个是返回错误,一个可以返回执行脚本后的返回值
JS调用App注册过的方法
再WKWebView里面注册供JS调用的方法,是通过WKUserContentController类下面的方法:
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
scriptMessageHandler是代理回调,JS调用name方法后,OC会调用scriptMessageHandler指定的对象。
JS在调用OC注册方法的时候要用下面的方式:
window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
注意,name(方法名)是放在中间的,messageBody只能是一个对象,如果要传多个值,需要封装成数组,或者字典。整个示例如下:
//OC注册供JS调用的方法
[[_webView configuration].userContentController addScriptMessageHandler:self name:@"closeMe"];
//OC在JS调用方法做的处理
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
NSLog(@"JS 调用了 %@ 方法,传回参数 %@",message.name,message.body);
}
//JS调用
window.webkit.messageHandlers.closeMe.postMessage(null);
如果你在self的dealloc打个断点,会发现self没有释放!这显然是不行的!谷歌后看到一种解决方法,如下:
@interface WeakScriptMessageDelegate : NSObject<WKScriptMessageHandler>
@property (nonatomic, weak) id<WKScriptMessageHandler> scriptDelegate;
- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate;
@end
@implementation WeakScriptMessageDelegate
- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate
{
self = [super init];
if (self) {
_scriptDelegate = scriptDelegate;
}
return self;
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
[self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
}
@end
思路是另外创建一个代理对象,然后通过代理对象回调指定的self,
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
[userContentController addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:@"closeMe"];
运行代码,self释放了,WeakScriptMessageDelegate却没有释放啊啊啊!
还需在self的dealloc里面 添加这样一句代码:
[[_webView configuration].userContentController removeScriptMessageHandlerForName:@"closeMe"];
坑 —> 页面提示没登陆 或者打开网页白屏 下面做出解答
关于Cookie
同一个应用,不同UIWebView之间的Cookie是自动同步的。它们都是保存在NSHTTPCookieStorage中。 当UIWebView加载一个URL的时候,在加载完成时候,这个URL response中包含的cookie会自动以NSHttpCookie的形式保存到 NSHTTPCookieStorage中。同时,如果在http response中,对cookie进行更新或者删除的话,其结果也会直接反应到NSHTTPCookieStorage 存储的cookie数据中。
//获取保存的cookie
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
//设置
[storage setCookies:cookies forURL:url mainDocumentURL:nil]
//删除
[storage deleteCookie:cookie]
WKWebView cookie同步
WKWebView的cookie并不会再加载url后自动保存到NSHTTPCookieStorage中。
原因是因为现在WKWebView会忽视默认的网络存储, NSURLCache, NSHTTPCookieStorage, NSCredentialStorage。 目前是这样的,WKWebView有自己的进程,同样也有自己的存储空间用来存储cookie和cache, 其他的网络类如NSURLConnection是无法访问到的。 同时WKWebView发起的资源请求也是不经过NSURLProtocol的,导致无法自定义请求。
同一个应用,不同WKWebView之间的cookie同步,可以通过使用同一个WKProcessPool实现cookie的同步。
1.创建ProcessPool的单例,来管理 WKProcessPool.
2. WKWebViewConfiguration *cofi = [[WKWebViewConfiguration alloc] init];
cofi.processPool = [ProcessPool share];
获取Cookie:
NSMutableString *cookieStr = [NSMutableString string];
for (NSHTTPCookie *cookie in NSHTTPCookieStorage.sharedHTTPCookieStorage.cookies) {
[cookieStr appendString:[NSString stringWithFormat:@"%@=%@",cookie.name,cookie.value]];
}
NSMutableURLRequest *requset = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"www.xxxx.com"]];
[requset addValue:cookieStr forHTTPHeaderField:@"Cookie"];
[self.webV loadRequest:requset];
更多可以去查看stackoverflow上回答:http://stackoverflow.com/questions/26573137/can-i-set-the-cookies-to-be-used-by-a-wkwebview/26577303#26577303
以下方法来自网络
+ (NSString *)cookiesForURLString:(NSString *)str{
NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:str]]; // 这里的HOST是你web服务器的域名地址
// 比如你之前登录的网站地址是abc.com(当然前面要加http://,如果你服务器需要端口号也可以加上端口号),那么这里的HOST就是http://abc.com
// 设置header,通过遍历cookies来一个一个的设置header
for (NSHTTPCookie *cookie in cookies){
// cookiesWithResponseHeaderFields方法,需要为URL设置一个cookie为NSDictionary类型的header,注意NSDictionary里面的forKey需要是@"Set-Cookie"
NSArray *headeringCookie = [NSHTTPCookie cookiesWithResponseHeaderFields:
[NSDictionary dictionaryWithObject:
[[NSString alloc] initWithFormat:@"%@=%@",[cookie name],[cookie value]]
forKey:@"Set-Cookie"]
forURL:[NSURL URLWithString:str]];
// 通过setCookies方法,完成设置,这样只要一访问URL为HOST的网页时,会自动附带上设置好的header
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookies:headeringCookie
forURL:[NSURL URLWithString:str]
mainDocumentURL:nil];
}
return str;
}
共享cookie (评论说 iOS 8 还是不能共享 没实测) 来源于奉强的个人博客 http://fengqiang.leanote.com/post/iOS开发-打通UIWebView和WKWebView的Cookie
1、思路
思路是这样子的,我们可以通过NSHTTPCookieStorage的一个单例,拿到app中所有的UIWebView的cookie,拿到之后再通过让WKWebView执行一段js代码,把这些cookie设置到WKWebView中,这样就可以实现WKWebView获取UIWebView的cookie了。
2、代码
1、新建一个单例
+ (instancetype)sharedWKCookieSyncManager {
static WKCookieSyncManager *sharedWKCookieSyncManagerInstance = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
sharedWKCookieSyncManagerInstance = [[self alloc] init];
});
return sharedWKCookieSyncManagerInstance;
}
2、在单例中,新建一个WKProcessPool对象,并且,需要保持这个对象为单例,因为在WKWebView中,只有使用了同一个WKProcessPool的WKWebView,才会共享cookie。
- (WKProcessPool *)processPool {
if (!_processPool) {
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
_processPool = [[WKProcessPool alloc] init];
});
}
return _processPool;
}
3、新建一个setCookie方法,在方法中,请求一个不存在的url,并且在回调中,设置cookie
- (void)setCookie {
//判断系统是否支持wkWebView
Class wkWebView = NSClassFromString(@"WKWebView");
if (!wkWebView) {
return;
}
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.processPool = self.processPool;
self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:self.testUrl];
self.webView.navigationDelegate = self;
[self.webView loadRequest:request];
}
4、在回调中,设置cookie
#pragma mark - WKNavigationDelegate
- (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;\
}";
//拼凑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];
}
四、补充
在后面的开发中,需要设置所有的WKWebView都使用前面那个单例processPool,否则,cookie是不会在WKWebView之间共享的。
再次补充
https://github.com/fq050766/WKWebViewAndUIWebViewCookiesDemo
在这里 作者说 在iOS9和iOS10上面测试,发现uiwebView和wkwebview、AFN的cookies都是互通的了 (没测试)