WKWebView使用与交互

作者: 守护地中海的花 | 来源:发表于2021-02-21 16:41 被阅读0次

    参考:iOS 原生与 JS 交互参考Demo地址

    tips:学点web技术是有必要的

    UIWebView已经被苹果爸爸抛弃了下面只说WKWebView其实方法思想差不多
    demo地址

    基础使用

    加载网页

    • 导入
    #import <WebKit/WebKit.h>
    
    • 懒加载
    @property(nonatomic,strong)WKWebView *webView;
    - (WKWebView *)webView
    {
        if (!_webView) {
            WKWebView *view = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
            [self.view addSubview:view];
            _webView = view;
        }
        return _webView;
    }
    
    • 加载本地、网络
    //网络
    NSString *url = @"https://www.baidu.com";
    [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]]];
    
    //本地
    NSString *filePath = [[NSBundle mainBundle]pathForResource:@"index" ofType:@"html"];
    NSURL *baseUrl = [[NSBundle mainBundle]bundleURL];
    NSString *sourceString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
    [self.webView loadHTMLString:sourceString baseURL:baseUrl];
    

    Swift版本:

    lazy var webView: WKWebView = {
        var webView = WKWebView(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height))
        self.view.addSubview(webView)
        return webView
    }()
    
    let url = "https://www.baidu.com"
    webView.load(URLRequest.init(url: URL.init(string: url)!))
    
    image.png
    image.png

    代理

    遵守代理 navigationDelegate,主要处理一些跳转、加载处理操作
    遵守代理 UIDelegate,主要处理JS脚本,确认框,警告框等

    navigationDelegate

    处理 跳转、加载等
    代理方法也很多 简单讲几条

    • 身份验证 基本不用
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;
    

    公司要对接一个第三方平台,然后就有了一个可奇葩的逻辑,用户填写完相关信息后,点击提交,然后服务器返回一个网页的源代码……需要用WebView加载这个网页。实现的时候发现,我自己写的简单的网页源码可以加载,但是服务器返回的就是无法加载。后来把源码保存成文件以后,用浏览器打开发现,该网页链接的站点是一个不受信任的站点,应该是因为服务器证书无效而不受信任。

    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler
    {
        NSLog(@"didReceiveAuthenticationChallenge");
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            NSURLCredential *card = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential,card);
        }
    }
    
    • decidePolicyForNavigationActiondecidePolicyForNavigationResponse
      请求之前是否跳转、请求响应之后是否跳转(方法会多次调用的 因为跳转就有)
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction preferences:(WKWebpagePreferences *)preferences decisionHandler:(void (^)(WKNavigationActionPolicy, WKWebpagePreferences *))decisionHandler API_AVAILABLE(macos(10.15), ios(13.0));
    

    2个方法任取其一

    typedef NS_ENUM(NSInteger, WKNavigationActionPolicy) {
        WKNavigationActionPolicyCancel,
        WKNavigationActionPolicyAllow,
    } API_AVAILABLE(macos(10.10), ios(8.0));
    
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
    {
        NSLog(@"decidePolicyForNavigationAction");
        decisionHandler(WKNavigationActionPolicyAllow);
    }
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
    {
        NSLog(@"decidePolicyForNavigationResponse");
        decisionHandler(WKNavigationResponsePolicyAllow);
    }
    

    WKNavigationActionPolicyAllow:网页可以正常跳转
    WKNavigationActionPolicyCancel:取消网页跳转
    此方法可以用于拦截url与web交互
    获取协议、域名、完整路径、相对路径、端口、路径、search、参数
    例子:

    NSLog(@"scheme:%@",navigationAction.request.URL.scheme);
    NSLog(@"host:%@",navigationAction.request.URL.host);
    NSLog(@"absoluteString:%@",navigationAction.request.URL.absoluteString);
    NSLog(@"relativePath:%@",navigationAction.request.URL.relativePath);
    NSLog(@"port:%@",navigationAction.request.URL.port);
    NSLog(@"path:%@",navigationAction.request.URL.path);
    NSLog(@"pathComponents:%@",navigationAction.request.URL.pathComponents);
    NSLog(@"query:%@",navigationAction.request.URL.query);
    NSLog(@"decidePolicyForNavigationAction");
    
    ------ ViewController.m ------ 65 行 ------ scheme:https
    ------ ViewController.m ------ 66 行 ------ host:hqhhtest.hqhh520.cn
    ------ ViewController.m ------ 67 行 ------ absoluteString:https://hqhhtest.hqhh520.cn/h5/#/carRental?classId=9
    ------ ViewController.m ------ 68 行 ------ relativePath:/h5
    ------ ViewController.m ------ 69 行 ------ port:(null)
    ------ ViewController.m ------ 70 行 ------ path:/h5
    ------ ViewController.m ------ 71 行 ------ pathComponents:(
        "/",
        h5
    )
    ------ ViewController.m ------ 72 行 ------ query:(null)
    

    如果加载本地界面 不会主动调用action、response方法

    • 页面开始加载内容返回、加载完成加载失败*
    - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation
    {
        NSLog(@"didStartProvisionalNavigation");
    }
    - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation
    {
        NSLog(@"didCommitNavigation");
    }
    - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
    {
        NSLog(@"didFinishNavigation");
    }
    - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
    {
        NSLog(@"didFailNavigation");
    }
    - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
    {
        //当Response响应后 不允许则会加载失败
        NSLog(@"didFailProvisionalNavigation");
    }
    

    UIDelegate

    处理JS脚本,确认框,警告框等
    js中加弹出框代码哦

    - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
    {
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提醒" message:message preferredStyle:UIAlertControllerStyleAlert];
        [alert addAction:[UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
            completionHandler();
        }]];
        [self presentViewController:alert animated:YES completion:nil];
    }
    

    小功能

    返回、前进上一个界面
    if (self.webView.canGoBack) {
        [self.webView goBack];
    }
    if (self.webView.canGoForward) {
        [self.webView goForward];
    }
    
    获取标题、获取加载进度

    记得销毁 dealloc 移除监听

    [_webView removeObserver:self forKeyPath:@"title"];
    [_webView removeObserver:self forKeyPath:@"estimatedProgress"];
    [_webView.scrollView removeObserver:self forKeyPath:@"contentSize"];
    - (void)addObserver
    {
        [self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];
        [self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:NULL];
        [self.webView.scrollView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew context:nil];
    }
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
    {
        NSLog(@"keyPath:%@",keyPath);
        if ([keyPath isEqualToString:@"title"]) {
            self.title = self.webView.title;
        } else if ([keyPath isEqualToString:@"estimatedProgress"]) {
            NSLog(@"%f",self.webView.estimatedProgress);
        } else if ([keyPath isEqualToString:@"contentSize"]) {
            NSLog(@"%@",object);
        } else {
            [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        }
    }
    

    交互

    url重定向

    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
    {
        NSLog(@"URL:%@",navigationAction.request.URL);
        decisionHandler(WKNavigationActionPolicyAllow);
    }
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
    {
        NSLog(@"decidePolicyForNavigationResponse");
        decisionHandler(WKNavigationResponsePolicyAllow);
    }
    

    判断url

    MessageHandler(原生)

    拓展点configuration、userContentController

    初始化设置

    - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
    
    //设置偏好设置
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc]init];
    // 默认是0 其实不建议在此设置的
    config.preferences.minimumFontSize = 10;
    // 是否支持javascript
    config.preferences.javaScriptEnabled = YES;
    //不通过用户交互,是否可以打开窗口
    config.preferences.javaScriptCanOpenWindowsAutomatically = NO;
    
    点击js原生响应

    为了测试:本地准备好html文件 这里的js方法 和 三方
    WebViewJavaScriptBridge写法略有不同哦

    为了测试:本地准备好html文件 这里的js方法 和 三方WebViewJavaScriptBridge写法略有不同哦
    为了测试:本地准备好html文件 这里的js方法 和 三方WebViewJavaScriptBridge写法略有不同哦

    window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
    
    image.png

    设置userContentController 遵守代理WKScriptMessageHandler 实现方法

    WKUserContentController *userContentController = config.userContentController;
    [userContentController addScriptMessageHandler:self name:@"showMobile"];
    [userContentController addScriptMessageHandler:self name:@"showName"];
    [userContentController addScriptMessageHandler:self name:@"showSendMsg"];
    

    移除

    WKUserContentController *controller = self.webView.configuration.userContentController;
    [controller removeScriptMessageHandlerForName:@"showMobile"];
    [controller removeScriptMessageHandlerForName:@"showName"];
    [controller removeScriptMessageHandlerForName:@"showSendMsg"];
    

    代理

    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
    {
        NSLog(@"%@",message.body);
        NSLog(@"%@",message.name);
    }
    
    原生驱动js响应 evaluateJavaScript

    可以编写几个按钮 去触发
    js代码


    image.png
    [self.webView evaluateJavaScript:@"alertMobile()" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
        //JS 返回结果
        NSLog(@"%@ %@",response,error);
    }];
    [self.webView evaluateJavaScript:@"alertName('wpp')" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
        //JS 返回结果
        NSLog(@"%@ %@",response,error);
    }];
    [self.webView evaluateJavaScript:@"alertSendMsg('wpp','20岁')" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
        //JS 返回结果
        NSLog(@"%@ %@",response,error);
    }];
    

    WebViewJavascriptBridge(三方)

    • 单独准备html文件
    • 最好都是最新的库 旧的或许会崩溃
    • 设置minimumFontSize是40 不然界面控件有点小
    • 不能单独设置navigationDelegate代理 因为bridge需要设置该代理
    • 设置属性
    #import "WebViewJavascriptBridge.h"
    @property(nonatomic,strong)WebViewJavascriptBridge *bridge;
    self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView];
    [self.bridge setWebViewDelegate:self];
    
    • 设置监听的webView
    - (void)addRegisterHandler
    {
        [self.bridge registerHandler:@"scanClick" handler:^(id data, WVJBResponseCallback responseCallback) {
            NSLog(@"扫一扫 %@",data);
            responseCallback(@"回调");
        }];
        [self.bridge registerHandler:@"locationClick" handler:^(id data, WVJBResponseCallback responseCallback) {
            NSLog(@"地址 %@",data);
            responseCallback(@"回调");
        }];
        [self.bridge registerHandler:@"colorClick" handler:^(id data, WVJBResponseCallback responseCallback) {
            NSLog(@"改变颜色 %@",data);
            responseCallback(@"回调");
        }];
        [self.bridge registerHandler:@"shareClick" handler:^(id data, WVJBResponseCallback responseCallback) {
            NSLog(@"分享%@",data);
            responseCallback(@"回调");
        }];
        [self.bridge registerHandler:@"payClick" handler:^(id data, WVJBResponseCallback responseCallback) {
            NSLog(@"支付 %@",data);
            responseCallback(@"回调");
        }];
        [self.bridge registerHandler:@"shakeClick" handler:^(id data, WVJBResponseCallback responseCallback) {
            NSLog(@"摇一摇 %@",data);
            responseCallback(@"回调");
        }];
        [self.bridge registerHandler:@"goback" handler:^(id data, WVJBResponseCallback responseCallback) {
            NSLog(@"返回 %@",data);
            responseCallback(@"回调");
        }];
    }
    
    • js互动
    [self.bridge callHandler:@"testJSFunction" data:@"一个字符串" responseCallback:^(id responseData) {
            NSLog(@"%@",responseData);
        }];
    

    demo地址

    小tip

    预算view的高度

    //避免高度不停回调
    @property(nonatomic,assign)CGFloat webViewHeight;
    //回调高度
    @property(nonatomic,copy)void (^refreshUIBlock)(void);
    if ([keyPath isEqualToString:@"contentSize"]) {
        if (self.webViewHeight == self.webView.scrollView.contentSize.height) {
            return;
        }
        self.webView.height = self.webView.scrollView.contentSize.height;
        self.height = self.webView.height;
        self.webViewHeight = self.webView.height;
        !self.refreshUIBlock ?: self.refreshUIBlock ();
        self.webViewHeight = self.webView.scrollView.contentSize.height;
    }
    

    进度条

    显示不出来添加到scrollview
    [self.webView.scrollView addSubview:self.progressView];

    @property(nonatomic,strong)UIProgressView *progressView;
    
    - (UIProgressView *)progressView
    {
        if (!_progressView) {
            UIProgressView *view = [[UIProgressView alloc]initWithFrame:CGRectMake(0, 0, self.width, 0)];
            [self.webView addSubview:view];
            view.progressTintColor = kThemeColor;
            //view.trackTintColor = [UIColor lightGrayColor];
            _progressView = view;
        }
        return _progressView;
    }
    
    if ([keyPath isEqualToString:@"estimatedProgress"]) {
        //NSLog(@"%f",self.webView.estimatedProgress);
        [self.progressView setProgress:self.webView.estimatedProgress animated:YES];
        self.progressView.hidden = self.webView.estimatedProgress == 1.0 ? YES : NO;
    }
    

    相关文章

      网友评论

        本文标题:WKWebView使用与交互

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