前言
WKWebView是iOS8.0之后推出的,相对于UIWebView而言,有着更快的渲染速度和更好的用户体验。我们主要从以下几方面入手来讨论WKWebView:
1.WKWebView常用的代理方法
2.网页加载进度的监控;
3.js调用native;
4.native调用js;
5.WKWebView当中跨域的问题;
6.WKWebView用户数据传递;
7.WKWebView清除缓存cookie;
一.WKWebView常用的代理方法
三个是否允许加载函数
/// 接收到服务器跳转请求之后调用 (服务器端redirect),不一定调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
/// 在收到服务器的响应头,根据response相关信息,决定是否跳转。decisionHandler必须调用,来决定是否跳转,参数WKNavigationActionPolicyCancel取消跳转,WKNavigationActionPolicyAllow允许跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
/// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
追踪加载过程函数:
/// 页面开始加载
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
/// 开始获取到网页内容时返回
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
/// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
/// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;
WKScriptMessageHandler:必须实现的函数,是APP与js交互,提供从网页中收消息的回调方法
/// message: 收到的脚本信息.
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
WKUIDelegate:UI界面相关,原生控件支持,三种提示框:输入、确认、警告。首先将web提示框拦截然后再做处理。
/// 创建一个新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;
/// 输入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler;
/// 确认框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
/// 警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;
二.网页加载进度的监控
当你的前端页面渲染比较慢的时候,进度条的显示主要是为了提升用户体验,通过UIProgressView和kvo的监听来实现:
设置监听
[self.wkwebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
更改进度条(如果这里没有处理kvo的话,会造成崩溃,同样dealloc的时候注意移除观察者)
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"estimatedProgress"]) {
self.progressView.progress = self.wkwebView.estimatedProgress;
if (self.progressView.progress == 1) {
self.progressView.hidden = YES;
}
}
}
移除观察者
- (void)dealloc {
[self.wkwebView removeObserver:self forKeyPath:@"estimatedProgress"];
}
三.js调用native
js调用native主要是前端同学来做,使用非常简单;通过methodName找到native注册好的方法进行调用,body是传过去的参数,body可以直接是字符串或字典或者数组这样的对象,但是我司的安卓说这样他们不能处理,所以我们统一用的是json的字符串,body建议作为字典控制,方面版本迭代的时候控制。
H5调用native
window.webkit.messageHandlers.methodName.postMessage(body)
native的方法注册和绑定,主要是通过WKScriptMessageHandler这个类,有个比较坑地方是如果直接和WKWebViewConfiguration绑定,会造成循环引用,控制器无法释放,需要弱引用一下。
创建弱引用的WKScriptMessageHandler
#import <Foundation/Foundation.h>
#import <WebKit/WKScriptMessageHandler.h>
@interface WeakScriptDelegate : NSObject<WKScriptMessageHandler>
@property (nonatomic, weak) id<WKScriptMessageHandler> scriptDelegate;
- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate;
@end
@implementation WeakScriptDelegate
- (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];
}
注入native方法
- (WKWebView *)wkwebView {
if (!_wkwebView) {
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.allowsInlineMediaPlayback = YES;
config.allowsPictureInPictureMediaPlayback = YES;
WeakScriptDelegate *weakDelegate = [[WeakScriptDelegate alloc] initWithDelegate:self];
[config.userContentController addScriptMessageHandler:weakDelegate name:@"约定好的方法"];
_wkwebView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];
_wkwebView.navigationDelegate = self;
_wkwebView.UIDelegate = self;
_wkwebView.backgroundColor = self.view.backgroundColor;
_wkwebView.scrollView.bounces = NO;
}
return _wkwebView;
}
处理js的调用,这个调用比UIWebView不要好用太多
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
DebugLog(@"JS invoke %@ method,param %@",message.name,message.body);
if ([message.name isEqualToString:@"方法名"]) {
}
}
四.native调用js;
NSString *startStr = @"方法名('参数')";
[weakSelf.wkwebView evaluateJavaScript: startStr completionHandler:^(NSString * _Nullable shareJson, NSError * _Nullable error) {
//可以处理回调结果和错误异常
}];
五.WKWebView当中跨域的问题;
WKWebView是不允许跨域访问的,但是可以有一些折中方案,比如可以跳到safari浏览器去完成跨域访问,或者将h5带有a链接去掉。
去掉a链接
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
if (!navigationAction.targetFrame.isMainFrame) {
[webView evaluateJavaScript:@"var a = document.getElementsByTagName('a');for(var i=0;i<a.length;i++){a[i].setAttribute('target','');}" completionHandler:nil];
}
decisionHandler(WKNavigationActionPolicyAllow);
}
六. WKWebView用户数据传递
由于WKWevView无法像jsexport那样被h5调用,所以有些用户数据上的交互要找其他方案,比如可以将参数拼接到url上面,但是url会有字节数的限制,可以注入到localStorage中,localStorage相当于客户端的userdefaults,以key-value的形式存在。
注入localStorage
NSString *token = [NSString stringWithFormat:@"localStorage.setItem(\"cookieLoginKey\",'%@')",localToken];
WKUserScript *tokenScript = [[WKUserScript alloc] initWithSource:token injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[self.userContentController addUserScript:tokenScript];
七. WKWebView清除缓存cookie
清除cookie
NSHTTPCookie *cookie;
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (cookie in [storage cookies]){
[storage deleteCookie:cookie];
}
清除缓存
NSURLCache *cache = [NSURLCache sharedURLCache];
[cache removeAllCachedResponses];
[cache setDiskCapacity:0];
[cache setMemoryCapacity:0];
NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{
}];
以上就是WKWebView常用的一些知识框架,欢迎大家讨论!
网友评论