iOS與h5交互可以分為兩部分,一部分是UIWebView與h5交互,一部分是WKWebView與h5交互,
1.UIWebView與h5交互
h5內(nèi)URL攔截
可以在代理方法
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
中攔截實(shí)現(xiàn)
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSString * urlString = request.URL.absoluteString;
NSString * scheme = request.URL.scheme;
// 可以在此處根據(jù)url地址,或者 scheme判斷是否需要攔截,返回NO,則不會(huì)跳轉(zhuǎn)
return YES;
}
為H5添加js方法
在代理方法
- (void)webViewDidFinishLoad:(UIWebView *)webView;
加載完頁面之后插入js代碼,如給h5頁面所有的圖片添加點(diǎn)擊事件(ps:如果可能的話還是讓前段自己加吧~~)
- (void)webViewDidFinishLoad:(UIWebView *)webView {
//添加圖片可點(diǎn)擊JS
[webView stringByEvaluatingJavaScriptFromString:@"function registerImageClickAction(){\
var imgs=document.getElementsByTagName('img');\
var length=imgs.length;\
for(var i=0;i<length;i++){\
img=imgs[i];\
img.onclick=function(){\
window.location.href='image-preview:'+this.src}\
}\
}"];
}
重點(diǎn),原生與h5交互,即:原生調(diào)用h5方法,h5調(diào)用原生方法
首先配置環(huán)境,原生要繼承JSExport協(xié)議(ps:需要導(dǎo)入頭文件
<JavaScriptCore/JavaScriptCore.h>
),并且添加協(xié)議方法提供給h5調(diào)用,如:
@protocol JSObjcDelegate <JSExport>
//不帶參數(shù)調(diào)用
- (void)call;
//帶參數(shù)調(diào)用
- (void)getCall:(NSString *)callString;
@end
然后在控制器中創(chuàng)建UIWebView并設(shè)置代理,實(shí)現(xiàn)剛剛聲明的協(xié)議 JSObjcDelegete,并設(shè)置一個(gè)全局的jsContext
@interface JKUIwebViewTestVC : UIViewController <UIWebViewDelegate, JSObjcDelegate>
@property (nonatomic, strong) JSContext *jsContext;
@property (strong, nonatomic) UIWebView *webView;
@end
在
- (void)webViewDidFinishLoad:(UIWebView *)webView
代理方法中設(shè)置jsContext,因?yàn)槿绻鹷ebView中有鏈接跳轉(zhuǎn),就必須要重新設(shè)置,否則h5無法調(diào)用原生方法,下方JKAPP是提供給h5調(diào)用的對(duì)象(PS:建議把所有的UI操作都放到主線程操作,否則的話有可能導(dǎo)致線程崩潰,是有可能~~~~)
self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
self.jsContext[@"JKAPP"] = self;
self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
context.exception = exceptionValue;
NSLog(@"異常信息:%@", exceptionValue);
};
最后實(shí)現(xiàn)JSObjcDelegete方法,就可以提供給h5調(diào)用了,原生可以用JSValue去調(diào)用h5方法,具體如下
- (void)call {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"" message:@"\(^o^)/~h5調(diào)用原生成功" preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction * cancleAction = [UIAlertAction actionWithTitle:@"確定" style:(UIAlertActionStyleCancel) handler:nil];
[alert addAction:cancleAction];
[self presentViewController:alert animated:YES completion:nil];
});
}
- (void)getCall:(NSString *)callString {
JSValue *Callback = self.jsContext[@"alerCallback"];
[Callback callWithArguments:@[@"原生去調(diào)用h5了"]];
}
h5可以這樣調(diào)用原生方法
<input type="button" value="h5喚起原生方法(call)"
onclick="JKAPP.call()">
</div>
<div>
<input type="button" value="h5喚起原生方法getCall:(NSString
*)callString傳值,并且原生調(diào)用h5方法" onclick="call()">
</div>
遇到問題總結(jié)
1.在代理方法中操作UI偶爾導(dǎo)致線程崩潰
解決方案:所有UI操作都放到主線程操作
2.jsContext對(duì)象注入失敗.
原因:h5在頁面加載時(shí)需要獲取一些原生內(nèi)的信息,此時(shí)頁面尚未加載,jsContext對(duì)象尚未注入,獲取信息失敗,js代碼不在加載
解決方案1.先是h5在頁面加載完成之后再去獲取jsContext對(duì)象獲取信息,但是嘗試失敗了,對(duì)象仍然沒有注入成功.然后h5在頁面加載完成時(shí)添加了一個(gè)短暫延時(shí),,然后再去獲取信息,成功解決問題.
解決方案2.猜測(cè),未證實(shí)!!!我們h5中是先加載頁面,最后加載js,此時(shí)頁面加載完成,js不一定加載完成,有可能導(dǎo)致js對(duì)象注入失敗,猜測(cè),先加載js,在加載頁面,然后在頁面加載完成時(shí)去獲取對(duì)象加載信息,有可能能解決問題.
JSValue *Callback = self.jsContext[@"alerCallback"]; [Callback callWithArguments:@[@"原生去掉用h5了"]];
此方法有可能導(dǎo)致線程崩潰,而且頻率不低.
解決辦法:
dispatch_async(dispatch_get_main_queue(), ^{
if (weakSelf.jsContext && data.length > 0) {
JSValue *Callback = weakSelf.jsContext[callBack];
[weakSelf.jsContext[@"setTimeout"] callWithArguments:@[Callback, @0, data]];
}
});
使用此方式去回調(diào)h5方法,暫未發(fā)現(xiàn)崩潰現(xiàn)象,注意
[weakSelf.jsContext[@"setTimeout"] callWithArguments:@[Callback, @0, data]];
其中以@0分割,前面是JSValue對(duì)象,后面是參數(shù),如果有多個(gè)參數(shù)可以使用數(shù)組
2.WKWebview與h5交互
h5內(nèi)URL攔截
可以在代理方法
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
中攔截實(shí)現(xiàn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSURL * url = navigationAction.request.URL;
NSString * scheme = [url scheme];
decisionHandler(WKNavigationActionPolicyAllow);
// decisionHandler(WKNavigationActionPolicyCancel);
}
需要注意的是,實(shí)現(xiàn)此方法必須要調(diào)用decisionHandler這個(gè)block否則會(huì)導(dǎo)致app崩潰,參數(shù)是枚舉類型,WKNavigationActionPolicyAllow
代表允許加載,WKNavigationActionPolicyCancel
代表不允許加載
原生調(diào)用h5方法,KWebView 提供了一個(gè)
evaluateJavaScript:completionHandler:
方法,可以在此方法中實(shí)現(xiàn)原生調(diào)用js.
- (void)getAlertCall {
NSString *jsStr = [NSString
stringWithFormat:@"alerCallback('%@')",@"原生調(diào)用h5中的js成功"];
[self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@----%@",result, error);
}];
}
PS:一定要注意,在WKWebView中alert被攔截掉了,網(wǎng)頁不能直接彈出來要在代理方法- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
中自己實(shí)現(xiàn)彈窗,并且要調(diào)用completionHandler
回調(diào),否則會(huì)導(dǎo)致程序崩潰
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
NSLog(@"%@", message);
completionHandler();
}
h5調(diào)用原生方法,controller需要實(shí)現(xiàn)
WKScriptMessageHandler
代理,然后設(shè)置[configuration.userContentController addScriptMessageHandler:self name:@"call"];
其中name為方法名.然后原生在代理- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
中檢測(cè)js回調(diào),js可以這么調(diào)用window.webkit.messageHandlers.call.postMessage("h5調(diào)用原生!");
,其中call是協(xié)商的方法名,postMessage為參數(shù)
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSLog(@"+++++%@++++%@", message.body, message.name);
}