iOS網路(二) WebView和WKWebview與JS的互動
阿新 • • 發佈:2019-12-25
一、WebView的相關簡單操作
self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
self.webView.delegate = self;
[self.view addSubview:self.webView];
NSURL *url = [[NSBundle mainBundle] URLForResource:@"index.html" withExtension:nil];
NSURLRequest *reqeust = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:reqeust];
複製程式碼
#pragma mark - UIWebViewDelegate
// 載入所有請求資料,以及控制是否載入
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(@"request = %@",request);
// 路由能力 --
NSLog(@"URL = %@",request.URL);
NSLog(@"scheme = %@",request.URL.scheme);
NSString *scheme = request.URL.scheme;
if ([scheme isEqualToString:@"heheorg"])
{
return NO;//可以根據要跳轉的scheme設定不往下面下載,在此攔截住一個路由
}
// 說明在此可以為所欲為,只要我有一套自己制定的方案
// request.URL.pathComponents 得到 oc 的方法和引數
/*例子:
NSArray *array = request.URL.pathComponents;
// 此處虛擬碼沒有做 非空處理 - 實際若沒有非空處理則很容易出bug
NSString *methodName = array[1];
if ([methodName isEqualToString:@"getSum"])
{
// array[2],array[3]
NSLog(@"%@-%@",array[2],array[3]);
}
[self performSelector:NSSelectorFromString(methodName) withObject:array afterDelay:0];
*/
return YES;
}
// 開始載入
- (void)webViewDidStartLoad:(UIWebView *)webView
{
// 進度條
NSLog(@"****************分界線****************");
NSLog(@"開始載入");
}
// 載入完成
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
// tittle
NSString *tittle = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
self.title = tittle;
NSLog(@"****************分界線****************");
NSLog(@"載入完成");
}
// 載入失敗
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
NSLog(@"****************分界線****************");
NSLog(@"載入失敗:%@",error);
}
// oc呼叫js方法
- (IBAction)didClickRightItemClick:(id)sender
{
NSLog(@"響應按鈕");
// 可用於重新整理頁面等功能
// 客戶端資訊傳入token
// js 需要加密等處理 -> 返回data(有可能是非同步,所以可以返回一個函式)
// 非同步 用於OC加密 ,oc加密完成後 JS才可以展示 (此處無例子)
// 混合開發
// 重定向 - H5 3次
NSString *str = [self.webView stringByEvaluatingJavaScriptFromString:@"showAlert('呵呵')('哈哈')"];
NSLog(@"%@",str);// js方法返回的資料
/*
js 程式碼:
<script>
function showAlert(name)
{
alert(name);
return function handle(str)
{
alert('我是一個彈窗'+name+str);
return name + '返回給OC';
}
}
</script>
*/
}
複製程式碼
二、JavaScriptCore
需匯入JavaScriptCore.framework
1、三個關鍵類
-
JSContext.h
提供了全域性的上下文
-
JSValue.h
主要是一些型別的強轉(jsvalue轉化成oc型別)
-
JSExport.h
主要用在js響應oc物件,通過實現協議達到的對映來使用
2、初級使用
- (void)viewDidLoad
{
[super viewDidLoad];
self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
self.webView.delegate = self;
[self.view addSubview:self.webView];
NSURL *url = [[NSBundle mainBundle] URLForResource:@"index.html" withExtension:nil];;
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[self.webView loadRequest:request];
}
#pragma mark - UIWebViewDelegate
// 載入完成
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
NSString *titlt = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
self.title = titlt;
//JSContext就為其提供著執行環境 H5上下文
JSContext *jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
[jsContext evaluateScript:@"var arr = ['doomfist','hehe','呵呵']"];// 定義全域性變數
// evaluateScript 可執行一段指令碼
// 清晰 --
// js程式碼中沒有showMessage函式,但js呼叫了showMessage函式
// 類似於kvc的形式,key為showMessage,value為^{}
// 所以此處可以定義一個showMessage函式,一旦註冊就會覆蓋js程式碼中的同名方法
// 儲存一段程式碼塊
jsContext[@"showMessage"] = ^{
NSLog(@"來了");
// 引數 (JS 帶過來的)
NSArray *args = [JSContext currentArguments];
NSLog(@"args = %@",args);
};
/*
js程式碼:
function showAlert()
{
alert("hello");
showMessage("點選了彈框按鈕...",arr);
}
*/
}
複製程式碼
3、JavaScriptCore - 用於解釋oc和js,是oc和js的橋樑
Js的windows中的值通過JavaScriptCore傳入oc的self.windows的runloop中變成jsValue型別
- (void)setupJS
{
// JS-OC
self.jsContext[@"showMessage"] = ^{
NSLog(@"來了");
// 引數 (JS 帶過來的)
NSArray *args = [JSContext currentArguments];
NSLog(@"args = %@",args);
NSLog(@"currentThis = %@",[JSContext currentThis]);
// 能夠監控當前Windows資訊 - 沒什麼用
NSLog(@"currentCallee = %@",[JSContext currentCallee]);
// 當前的閉包的資料 - 用處不大
// OC-JS
NSDictionary *dict = @{@"name":@"呵呵",@"age":@18};
[[JSContext currentContext][@"ocCalljs"] callWithArguments:@[dict,@"呵呵噠"]];
// 調方法並傳參
};
//JS-OC
self.jsContext[@"showDict"] = ^{
NSLog(@"來了");
// 引數 (JS 帶過來的)
NSArray *args = [JSContext currentArguments];
NSLog(@"args = %@",args);
};
}
/* js程式碼
function ocCalljs(dict,str)
{
var name = dict['name'];
var age = dict['age'];
alert(name + age + str);
// 傳回去
showDict(dict)
}
*/
複製程式碼
4、JS操作物件
// JS 操作物件
JSObject *Object = [[JSObject alloc] init];
self.jsContext[@"Object"] = Object;
NSLog(@"Object == %d",[Object getSum:20 num2:40]);
/* js程式碼
function openAlbumImage()
{
getImage();
Object.letShowImage();
alert(Object.getS(10,30));
}
*/
// ---------
<JSExport>
- (void)letShowImage;
// 協議 - 協議方法
JSExportAs(getS,-(int)getSum:(int)num1 num2:(int)num2);
// ---------
- (void)letShowImage
{
NSLog(@"開啟相簿,上傳圖片");
}
- (int)getSum:(int)num1 num2:(int)num2
{
NSLog(@"來了");
// int (nil) - jsvalue (0)
return num1+num2;
}
// ---------
複製程式碼
三、WKWebview基本用法
//載入
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
// 在這裡插入下面的部分↓
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
// 兩個重要的代理 下面細講
self.webView.navigationDelegate = self;
self.webView.UIDelegate = self;
[self.view addSubview:self.webView];
NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"index.html" ofType:nil];
NSURL *fileURL = [NSURL fileURLWithPath:urlStr];
[self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL];
複製程式碼
// 這裡的內容插入上面↑ 如不插入則不會顯示手機應該顯示的正常頁面
NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name','viewport'); meta.setAttribute('content','width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
WKUserContentController *wkUController = [[WKUserContentController alloc] init];
[wkUController addUserScript:wkUScript];// 新增指令碼
configuration.userContentController = wkUController;
複製程式碼
// 互動
- (IBAction)didClickRightItemAction:(id)sender
{
// 調方法,傳引數
NSString *jsStr = [NSString stringWithFormat:@"showAlert('%@')",@"登陸成功"];
// oc呼叫js 直接執行指令碼
[self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result,NSError * _Nullable error) {
NSLog(@"%@----%@",result,error);
}];
}
複製程式碼
// 代理
#pragma mark - WKUIDelegate
- (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];
}
#pragma mark - WKNavigationDelegate
// 載入完成的代理
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
self.title = webView.title;
NSString *jsStr2 = @"var arr = [3,'abc']; ";
[self.webView evaluateJavaScript:jsStr2 completionHandler:^(id _Nullable result,error);
}];
}
// 攔截
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
NSURL *URL = navigationAction.request.URL;
NSString *scheme = [URL scheme];
if ([scheme isEqualToString:@"hehe"])
{
NSString *host = [URL host];
if ([host isEqualToString:@"jsCallOC"])
{
NSMutableDictionary *temDict = [self decoderUrl:URL];
NSString *username = [temDict objectForKey:@"username"];
NSString *password = [temDict objectForKey:@"password"];
NSLog(@"%@---%@",username,password);
}
else
{
NSLog(@"不明地址 %@",host);
}
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
複製程式碼
// 另一種oc呼叫js
// 一定要remove,不然會迴圈引用
// self - webView - configuration - userContentController - self
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"messgaeOC"];
// addScriptMessageHandler此處註冊WKScriptMessageHandler代理
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"messgaeOC"];
}
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
if (message.name)
{
// OC 層面的訊息
}
NSLog(@"message == %@ --- %@",message.name,message.body);
}
/* js程式碼
function messgaeHandle()
{
window.webkit.messageHandlers.messgaeOC.postMessage("hehe 訊息");
}
*/
複製程式碼
附:
wkwebview的代理的常用方法
//#pragma mark - WKNavigationDelegate
////請求之前,決定是否要跳轉:使用者點選網頁上的連結,需要開啟新頁面時,將先呼叫這個方法。
//- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
////接收到相應資料後,決定是否跳轉
//- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
////頁面開始載入時呼叫
//- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation;
//// 主機地址被重定向時呼叫
//- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation;
//// 頁面載入失敗時呼叫
//- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
//// 當內容開始返回時呼叫
//- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation;
//// 頁面載入完畢時呼叫
//- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation;
////跳轉失敗時呼叫
//- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
//// 如果需要證書驗證,與使用AFN進行HTTPS證書驗證是一樣的
//- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition,NSURLCredential *__nullable credential))completionHandler;
////9.0才能使用,web內容處理中斷時會觸發
//- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView NS_AVAILABLE(10_11,9_0);
複製程式碼
四、WebViewJavaScriptBridge
self.wjb = [WebViewJavascriptBridge bridgeForWebView:self.webView];
// 如果你要在VC中實現 UIWebView的代理方法 就實現下面的程式碼(否則省略)
[self.wjb setWebViewDelegate:self];
// 需要前端(js)適配
[self.wjb registerHandler:@"jsCallsOC" handler:^(id data,WVJBResponseCallback responseCallback) {
NSLog(@"currentThread == %@",[NSThread currentThread]);
NSLog(@"data == %@ -- %@",data,responseCallback);
}];
複製程式碼
// 內部邏輯
+ (instancetype)bridge:(id)webView
{
#if defined supportsWKWebView
if ([webView isKindOfClass:[WKWebView class]])
{
return (WebViewJavascriptBridge*) [WKWebViewJavascriptBridge bridgeForWebView:webView];
}
#endif
if ([webView isKindOfClass:[WVJB_WEBVIEW_TYPE class]])
{
WebViewJavascriptBridge* bridge = [[self alloc] init];
[bridge _platformSpecificSetup:webView];
return bridge;
}
[NSException raise:@"BadWebViewType" format:@"Unknown web view type."];
return nil;
}
複製程式碼
- (IBAction)didClickLeftAction:(id)sender
{
dispatch_async(dispatch_get_global_queue(0,0),^{
[self.wjb callHandler:@"OCCallJSFunction" data:@"oc呼叫JS咯" responseCallback:^(id responseData) {
NSLog(@"currentThread == %@",[NSThread currentThread]);
NSLog(@"呼叫完JS後的回撥:%@",responseData);
}];
});
}
複製程式碼