參考:iOS 原生與 JS 交互击碗、 參考Demo地址
tips:學(xué)點(diǎn)web技術(shù)是有必要的
UIWebView已經(jīng)被蘋果爸爸拋棄了下面只說WKWebView其實(shí)方法思想差不多
demo地址
基礎(chǔ)使用
加載網(wǎng)頁
- 導(dǎo)入
#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;
}
- 加載本地戒良、網(wǎng)絡(luò)
//網(wǎng)絡(luò)
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)!))
代理
遵守代理 navigationDelegate,主要處理一些跳轉(zhuǎn)、加載處理操作
遵守代理 UIDelegate,主要處理JS腳本滴肿,確認(rèn)框,警告框等
navigationDelegate
處理 跳轉(zhuǎn)湖员、加載等
代理方法也很多 簡單講幾條
- 身份驗(yàn)證 基本不用
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;
公司要對接一個(gè)第三方平臺贫悄,然后就有了一個(gè)可奇葩的邏輯,用戶填寫完相關(guān)信息后娘摔,點(diǎn)擊提交窄坦,然后服務(wù)器返回一個(gè)網(wǎng)頁的源代碼……需要用WebView加載這個(gè)網(wǎng)頁。實(shí)現(xiàn)的時(shí)候發(fā)現(xiàn)凳寺,我自己寫的簡單的網(wǎng)頁源碼可以加載鸭津,但是服務(wù)器返回的就是無法加載。后來把源碼保存成文件以后肠缨,用瀏覽器打開發(fā)現(xiàn)逆趋,該網(wǎng)頁鏈接的站點(diǎn)是一個(gè)不受信任的站點(diǎn),應(yīng)該是因?yàn)榉?wù)器證書無效而不受信任晒奕。
- (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);
}
}
-
decidePolicyForNavigationAction闻书、decidePolicyForNavigationResponse
請求之前是否跳轉(zhuǎn)、請求響應(yīng)之后是否跳轉(zhuǎn)(方法會多次調(diào)用的 因?yàn)樘D(zhuǎn)就有)
- (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個(gè)方法任取其一
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:網(wǎng)頁可以正常跳轉(zhuǎn)
WKNavigationActionPolicyCancel:取消網(wǎng)頁跳轉(zhuǎn)
此方法可以用于攔截url與web交互
獲取協(xié)議脑慧、域名魄眉、完整路徑、相對路徑闷袒、端口坑律、路徑、search囊骤、參數(shù)
例子:
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)
如果加載本地界面 不會主動(dòng)調(diào)用action晃择、response方法
- 頁面開始加載、內(nèi)容返回也物、加載完成宫屠、加載失敗*
- (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
{
//當(dāng)Response響應(yīng)后 不允許則會加載失敗
NSLog(@"didFailProvisionalNavigation");
}
UIDelegate
處理JS腳本,確認(rèn)框滑蚯,警告框等
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];
}
小功能
返回激况、前進(jìn)上一個(gè)界面
if (self.webView.canGoBack) {
[self.webView goBack];
}
if (self.webView.canGoForward) {
[self.webView goForward];
}
獲取標(biāo)題、獲取加載進(jìn)度
記得銷毀 dealloc 移除監(jiān)聽
[_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(原生)
拓展點(diǎn)configuration膘魄、userContentController
初始化設(shè)置
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
//設(shè)置偏好設(shè)置
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc]init];
// 默認(rèn)是0 其實(shí)不建議在此設(shè)置的
config.preferences.minimumFontSize = 10;
// 是否支持javascript
config.preferences.javaScriptEnabled = YES;
//不通過用戶交互,是否可以打開窗口
config.preferences.javaScriptCanOpenWindowsAutomatically = NO;
點(diǎn)擊js原生響應(yīng)
為了測試:本地準(zhǔn)備好html文件 這里的js方法 和 三方
WebViewJavaScriptBridge寫法略有不同哦
為了測試:本地準(zhǔn)備好html文件 這里的js方法 和 三方WebViewJavaScriptBridge寫法略有不同哦
為了測試:本地準(zhǔn)備好html文件 這里的js方法 和 三方WebViewJavaScriptBridge寫法略有不同哦
window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
設(shè)置userContentController 遵守代理WKScriptMessageHandler
實(shí)現(xiàn)方法
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);
}
原生驅(qū)動(dòng)js響應(yīng) evaluateJavaScript
可以編寫幾個(gè)按鈕 去觸發(fā)
js代碼
[self.webView evaluateJavaScript:@"alertMobile()" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
//JS 返回結(jié)果
NSLog(@"%@ %@",response,error);
}];
[self.webView evaluateJavaScript:@"alertName('wpp')" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
//JS 返回結(jié)果
NSLog(@"%@ %@",response,error);
}];
[self.webView evaluateJavaScript:@"alertSendMsg('wpp','20歲')" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
//JS 返回結(jié)果
NSLog(@"%@ %@",response,error);
}];
WebViewJavascriptBridge(三方)
- 單獨(dú)準(zhǔn)備html文件
- 最好都是最新的庫 舊的或許會崩潰
- 設(shè)置minimumFontSize是40 不然界面控件有點(diǎn)小
- 不能單獨(dú)設(shè)置navigationDelegate代理 因?yàn)閎ridge需要設(shè)置該代理
- 設(shè)置屬性
#import "WebViewJavascriptBridge.h"
@property(nonatomic,strong)WebViewJavascriptBridge *bridge;
self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView];
[self.bridge setWebViewDelegate:self];
- 設(shè)置監(jiān)聽的webView
- (void)addRegisterHandler
{
[self.bridge registerHandler:@"scanClick" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"掃一掃 %@",data);
responseCallback(@"回調(diào)");
}];
[self.bridge registerHandler:@"locationClick" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"地址 %@",data);
responseCallback(@"回調(diào)");
}];
[self.bridge registerHandler:@"colorClick" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"改變顏色 %@",data);
responseCallback(@"回調(diào)");
}];
[self.bridge registerHandler:@"shareClick" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"分享%@",data);
responseCallback(@"回調(diào)");
}];
[self.bridge registerHandler:@"payClick" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"支付 %@",data);
responseCallback(@"回調(diào)");
}];
[self.bridge registerHandler:@"shakeClick" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"搖一搖 %@",data);
responseCallback(@"回調(diào)");
}];
[self.bridge registerHandler:@"goback" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"返回 %@",data);
responseCallback(@"回調(diào)");
}];
}
- js互動(dòng)
[self.bridge callHandler:@"testJSFunction" data:@"一個(gè)字符串" responseCallback:^(id responseData) {
NSLog(@"%@",responseData);
}];
小tip
預(yù)算view的高度
//避免高度不徒呋洌回調(diào)
@property(nonatomic,assign)CGFloat webViewHeight;
//回調(diào)高度
@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;
}
進(jìn)度條
顯示不出來添加到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;
}