美文网首页
WKWebView的日常开发和使用

WKWebView的日常开发和使用

作者: 迷了jiang | 来源:发表于2019-06-17 13:56 被阅读0次

前言

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常用的一些知识框架,欢迎大家讨论!

相关文章

网友评论

      本文标题:WKWebView的日常开发和使用

      本文链接:https://www.haomeiwen.com/subject/agdzbftx.html