UIWebView 的 native交互
1. 实际开发中, 经常会通过前端webview来实现一些与native代码的交互功能,怎么实现呢?通常怎么做?有哪些实例枚举?
2. 这里说明一下常用方法有两种思路, 一个是直接使用JavaScriptCore框架,可以实现javascript和native代码的交互;还要一种思路是直接通过WebView的代理方法中添加代码或者解析URL来达到目的,本文介绍第二种。
一 、使用webview 怎样实现呢 ? 先介绍三个方法:(UIWebView的代理方法两个,UIWebView 提供的实例方法一个)
a. webview被触动加载之前会调用下面的代理。
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
b. webview加载完毕之后会调用的代理
- (void)webViewDidFinishLoad:(UIWebView *)webView;
c. 直接使用webview实例为自己的html文本 注入javascript代码 或者 调用javascript代码 都是用这个方法。
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
3. 直接上一块代码,简单演示怎样利用UIWebView的代理和实例方法实现 webview与native代码的交互。看看它们都是怎样运用的吧:
————
-(void)webViewDidFinishLoad:(UIWebView *)webView{
/*通过js 方法执行获取webview的高度! */
int h = [[webView stringByEvaluatingJavaScriptFromString:@"document.height"] floatValue];
webView.userInteractionEnabled = h > self.height;
webView.height = h;
self.height = MAX(h, k_SpanLeft);
/*取消长按webView上的链接弹出actionSheet的问题*/
[webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitTouchCallout = 'none';"];
/*js方法遍历图片添加点击事件+返回图片个数*/
static NSString * const jsGetImages =
@"function getImages(){\
var objs = document.getElementsByTagName(\"img\");\
for(var i=0;i<objs.length;i++){\
objs[i].οnclick=function(){\
document.location=\"myweb:imageClick:\"+this.src;\
};\
};\
return objs.length;\
};";
[webView stringByEvaluatingJavaScriptFromString:jsGetImages];//注入js方法
//注入自定义的js方法后别忘了调用 否则不会生效
[webView stringByEvaluatingJavaScriptFromString:@"getImages()"];
}
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
/// 对url的截获解析 通常在这个方法中获取url。
NSString *urlStr = request.URL.absoluteString;
TBLog(@"%@",urlStr);
if ([urlStr isEqualToString:@"about:blank"]) {
return YES;
}
/* 解释一下,这个方法,每当用户触动webview上的空间,webview需要刷新的时候都会调用这个方法,返回值决定是否对页面进行刷新,上面通过@“about:blank”的校验,如果过是空白页,应当返回YES,让webview进行刷新。 余下情况看看是否需要返回NO吧?
这里简单说明一下返回NO 的原因:
1. 前提是这个界面第一遍加载的时候已经完成了所有视图的更新
2. 对webview上空间的操作执行,会调用这个方法,但是界面不需要进行刷新(没有视图变化)
3. 满足以上两个条件,然后这样做,减少了布标要的webview刷新操作,也实现了可能需要的拦截和阻断(主要是为了阻断后续的webViewDidFinishLoad方法执行)。
4. 这个拦截很多时候是有必要的,比如点击一个url,而这个url的目的不是用来加载,只是用来传递信息,这时候就应该拦截,然后做解析,如下:
*/
return [self linkFromLinkUrl:request.URL];
/*
1.根据前面如果注入的获取网页界面图片的 javascript代码,那么点击图片的时候回触动下面方法的执行。
2.下面方法的逻辑是 根据图片 路径 匹配本地数组中图片路径得到图片索引位置,关键还是前面给图片添加了点击事件在这里触发。
*/
if ([urlStr hasPrefix:@"myweb:imageClick:"]) {
NSString *imageUrl = [urlStr substringFromIndex:@"myweb:imageClick:".length];
TBLog(@"image url------%@", imageUrl);
for (int i=0; i<self.arrImgElement.count; i++) {
TFHppleElement *tfhElement = self.arrImgElement[i];
NSString *tempImgUrl = [tfhElement.attributes objectForKey:@"src"];
if ([imageUrl isEqualToString:tempImgUrl]) {
if ([self.delegate respondsToSelector:@selector(htmlViewEvent:withInfo:)]) {
[self.delegate htmlViewEvent:RichViewEventImgClick withInfo:[NSString stringWithFormat:@"%d",i]];
}
break;
}
}
return NO;
}
return NO;
}
-(BOOL)linkFromLinkUrl:(NSURL *)linkUrl{
NSString *url = linkUrl.absoluteString;
if(parse url in order){
return NO;
} else if(parse url need link continue){
return YES;
}
//如果是其他的超链接,启动safari进行跳转
else (self.canOpenSafari) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:urlStr]];
}
return NO;
}
结语:通过拦截URL解析和Javascript代码的注入执行,可以实现webview 到 native 的一个信息传递,但是能够感受到一定的局限性(如果我们想要传递一个son数据,使用url显然不是很方便,甚至因为url的规则限制而变成不可能)。这个可以通过JavaScriptCore框架来解决。另外添加javascript注入和调用javascript代码都使用唯一的一个函数stringByEvaluatingJavaScriptFromString进行动态添加(显然可以获取到所有html文本后字符串注入进去,但是麻烦会接踵而至的!),然而如果我们需要更多的javascript代码和native方法的交互性,动态性,和将更多权限交给前端代码而不是应用的native代码,那么这个法子显然也不是很方便,这个时候也需要考虑直接使用JavaScriptCore来解决了。
综合而言, 这些技巧可能很有用,局限是把最大权限交给了native代码。