前端开发那些事儿

WKWebView使用与交互

2021-02-21  本文已影响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);
    }
}
- (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(三方)

#import "WebViewJavascriptBridge.h"
@property(nonatomic,strong)WebViewJavascriptBridge *bridge;
self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView];
[self.bridge setWebViewDelegate:self];
- (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(@"回调");
    }];
}
[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;
}
上一篇下一篇

猜你喜欢

热点阅读