Hybrid App: 对比UIWebView和WebKit实现JavaScript与Native交互
一、简介
在前面一篇文章中讲到过实现JavaScript与Native交互的方式有一种就是使用原生内嵌webView。在iOS8之前,开发者只能使用苹果提供的UIWebView类来加载URL或者HTML网页视图,然后通过设置代理,在回调函数中拦截并处理自定义交互事件,功能十分有限,通常只是作为一个辅助视图使用。在iOS8之后,苹果对这方面的技术进行了重构和优化,推出了一个新的框架WebKit。WebKit提供了Native与JavaScript交互的方法,整个框架的结构很清晰,对外暴露的接口友好实用,极大地方便了开发者实现网页视图和的Native交互。并且,WebKit框架采用导航堆栈的模式来管理网页视图的跳转,对于网页视图的管理和渲染,开发者更加容易管控。慢慢地,咱来比较这两种webView的使用区别。
二、UIWebView
1、UIWebView的详细构成
UIWebView的类构成之:属性
//id类型,遵守UIWebViewDelegate协议
@property (nullable, nonatomic, assign) id <UIWebViewDelegate> delegate //只读属性,webView内部的滚动视图
@property (nonatomic, readonly, strong) UIScrollView *scrollView //只读属性,是否可以后退
@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack //只读属性,是否可以前进
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward //只读属性,是否正在加载
@property (nonatomic, readonly, getter=isLoading) BOOL loading //是否支持缩放页面自适应
@property (nonatomic) BOOL scalesPageToFit //是否检测电话号码(链接形式)
@property (nonatomic) BOOL detectsPhoneNumbers //枚举类型,数据检测类型。如电话、邮箱等
@property (nonatomic) UIDataDetectorTypes dataDetectorTypes //是否使用内联播放器播放视频
@property (nonatomic) BOOL allowsInlineMediaPlayback //视频是否自动播放
@property (nonatomic) BOOL mediaPlaybackRequiresUserAction //是否支持air play功能
@property (nonatomic) BOOL mediaPlaybackAllowsAirPlay //是否将数据加载到内存后渲染界面
@property (nonatomic) BOOL suppressesIncrementalRendering //是否支持用户打开键盘进行交互
@property (nonatomic) BOOL keyboardDisplayRequiresUserAction //是否支持视频画中画
@property (nonatomic) BOOL allowsPictureInPictureMediaPlayback //是否支持链接预览
@property (nonatomic) BOOL allowsLinkPreview //页面长度
@property (nonatomic) CGFloat pageLength //页面间距
@property (nonatomic) CGFloat gapBetweenPages //页面数量
@property (nonatomic, readonly) NSUInteger pageCount //枚举类型,分页模式
@property (nonatomic) UIWebPaginationMode paginationMode //枚举类型,决定加载页面具有CSS属性时是采用页样式还是类样式
@property (nonatomic) UIWebPaginationBreakingMode paginationBreakingMode
UIWebView的类构成之:方法
//加载URL类型的webView
- (void)loadRequest:(NSURLRequest *)request //加载HTML类型的webView
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL //加载NSData类型的webView
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL //刷新webView
- (void)reload //停止加载webView
- (void)stopLoading //返回上一页
- (void)goBack //前进下一页
- (void)goForward //调用JavaScript代码
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script
UIWebView的类构成之:协议
//即将对网页URL发送请求,通过返回布尔值决定是否跳转,根据scheme或者navigationType匹配来做拦截处理,以完成端上的交互行为
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType //网页已经开始加载时调用
- (void)webViewDidStartLoad:(UIWebView *)webView //网页完成加载时调用
- (void)webViewDidFinishLoad:(UIWebView *)webView //网页加载失败时调用
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
UIWebView的类构成之:枚举
//导航类型
UIWebViewNavigationType
UIWebViewNavigationTypeLinkClicked //触击一个链接
UIWebViewNavigationTypeFormSubmitted //提交一个表单
UIWebViewNavigationTypeBackForward //触击前进或返回按钮
UIWebViewNavigationTypeReload //触击重新加载的按钮
UIWebViewNavigationTypeFormResubmitted //用户重复提交表单
UIWebViewNavigationTypeOther //发生其它行为 //翻页模式
UIWebPaginationMode
UIWebPaginationModeUnpaginated //无分页
UIWebPaginationModeLeftToRight //从左往右翻页
UIWebPaginationModeTopToBottom //从下往上翻页
UIWebPaginationModeBottomToTop //从上往下翻页
UIWebPaginationModeRightToLeft //从右往左翻页 //CSS模式
UIWebPaginationBreakingMode
UIWebPaginationBreakingModePage //页样式
UIWebPaginationBreakingModeColumn //列样式
2、UIWebView的使用,混合开发
[2-1] 创建一个HTML文件
<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title> <style>
.divcss{ border:1px solid #F00; width:300px; height:200px}
</style> <script type="text/javascript">
function showAlert(){
alert("我被成功调起来了"); //<!-- JS通知WKWebView, methodInvoke就是和端上约定的方法名称 -->
var list = [1,2,3];
var dict = {"name":"XYQ", "qq":"1273843", "list":list};
window.webkit.messageHandlers.methodInvoke.postMessage(dict); } function disp_confirm()
{
var r=confirm("Press a button")
if (r==true){
document.write("You pressed OK!")
}
else{
document.write("You pressed Cancel!")
}
} function disp_prompt()
{
var name=prompt("Please enter your name","")
if (name!=null && name!=""){
document.write("Hello " + name + "!")
}
} </script> </head> <body> <div class="divcss">
<!-- 这是一个链接跳转地址,点击时,端上将会进行拦截,弹出日志 -->
<a href="http://www.w3school.com.cn/">Visit W3School</a>
<br>
<br>
<a href="parent://www.parent.com.cn/">open parent</a>
</div> <input type="button" onclick="disp_confirm()"
value="Display a confirm box" /> <input type="button" onclick="disp_prompt()"
value="Display a prompt box" /> </body> </html>
[2-2] 创建并加载webView
- (void)viewDidLoad {
[super viewDidLoad]; //创建webView
self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
self.webView.delegate = self; //创建html
NSString *file = [[NSBundle mainBundle] pathForResource:@"web" ofType:@"html"];
NSString *html = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil]; //加载webView
[self.webView loadHTMLString:html baseURL:nil];
[self.view addSubview:self.webView];
}
[2-3] 设置代理,事件拦截
#pragma mark - UIWebViewDelegate
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { NSLog(@"------------网页即将开始加载------------"); //实际工作中,获取scheme或者navigationType,通过匹配scheme或者navigationType来拦截页面中的点击行为来完成端上交互
//这个scheme可以自定义的,不过需要跟前端约定好。例如"http"、"https"、"iOS"等等 , 例子如:"iOS://baidu.com/con/"
//1、点击链接
if (navigationType == UIWebViewNavigationTypeLinkClicked) { // <a href="http://www.w3school.com.cn/">Visit W3School</a> NSString *scheme = request.URL.scheme;
if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"]) { //H5调用原生方法,显示iOS的弹框
NSString *href = request.URL.absoluteString;
[self showAlertView:href]; }
return NO; //取消网页加载, 那么该链接就不会进行跳转了
} //2、点击按钮
if (navigationType == UIWebViewNavigationTypeFormSubmitted) { //H5调用原生方法,打开相册
[self openPhotoLibrary]; //取消网页加载, 那么该提交事件就不会触发了
return NO;
} return YES;
} -(void)webViewDidStartLoad:(UIWebView *)webView {
NSLog(@"------------网页已经开始加载------------");
} -(void)webViewDidFinishLoad:(UIWebView *)webView {
NSLog(@"------------网页已经完成加载------------"); //原生调用js方法,显示window的alert内容
[self showWindowAlertString];
} -(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
NSLog(@"------------网页已经加载失败------------");
}
[2-4] 原生方法和JS事件
#pragma mark - h5 call native
-(void)openPhotoLibrary { //打开Native系统相册
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
picker.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:picker animated:YES completion:nil];
} -(void)showAlertView:(NSString *)message { //打开Native系统弹框
UIAlertController *aletVc = [UIAlertController alertControllerWithTitle:@"提示" message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[aletVc addAction:action];
[self presentViewController:aletVc animated:YES completion:nil];
} #pragma mark - native call js
-(void)showWindowAlertString { //显示H5页面中的被调用的JS函数返回的弹框内容
NSString *result = [self.webView stringByEvaluatingJavaScriptFromString:@"alert()"];
[self showAlertView:result];
}
[2-5] 结果分析和显示
通过html格式即将加载webView时,代理方法会依次调用123。除非加载失败,才会打印出4;
加载成功后,此时原生调用JS代码,页面会弹出调用js函数返回的内容;
点击链接时,本来应该跳转到新的页面,但是端上在即将加载页面做了拦截,返回值设置了NO,所以不会跳转;
点击按钮时,本来HTML页面的js事件会触发,但是由于端上在即将加载页面做了拦截,返回值设置了NO,所以不会触发js事件;
//首次加载HTM
-- ::19.402276+ WebView[:] ------------网页即将开始加载------------
-- ::19.403887+ WebView[:] ------------网页已经开始加载------------
-- ::19.457337+ WebView[:] ------------网页已经完成加载------------ //点击链接
-- ::23.674148+ WebView[:] ------------网页即将开始加载------------ //点击按钮
-- ::28.682868+ WebView[:] ------------网页即将开始加载------------
三、WebKit
1、可以看到使用UIWebView虽然可以实现Native与H5/JS的交互,但是功能过于简单,而且在iOS系统快速升级之后,UIWebView已经被苹果摒弃,不再被推荐使用,取而代之的则是WebKit框架,就跟UserNotification框架取代UILocalNotification类一样。WebKit框架用到的类很多,设计的思想更加面向对象化,模块化,开发者使用起来非常方便,代码结构清晰。WebKit框架图大致如下:
2、看完上面的WebKit框架图是不是感觉很清晰,每一个模块有自己的对应的职责,WKWebView很具需要依赖这些模块,基本可以满足开发者想要的大多数需求。现在对一些重要的类概念了解一下。
3、WKWebView属性和方法与UIWebView有很多相类似,具体的类的信息可以看API。
3-1 WKWebView提供了进度progress属性,可以使用KVO监控页面加载的进度,这个优化能提供用户一个更好的使用体验,而这个在UIWebView中是没有的。
/*! @abstract An estimate of what fraction of the current navigation has been completed.
@discussion This value ranges from 0.0 to 1.0 based on the total number of
bytes expected to be received, including the main document and all of its
potential subresources. After a navigation completes, the value remains at 1.0
until a new navigation starts, at which point it is reset to 0.0.
@link WKWebView @/link is key-value observing (KVO) compliant for this
property.
*/
@property (nonatomic, readonly) double estimatedProgress;
3-2 其次,WKWebView相比于UIWebView在导航管理上是更优秀的,采用堆栈管理的方式,能够任意进行不同视图之间的跳转。这个属性就是WKBackForwardList,表示的是所有的网页视图堆栈,管理每一个网页视图节点,可以看看它的构成如下:
//去某一个网页节点: WKWebView的方法,跳转到某一个网页节点
- (nullable WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item;
//这个WKBackForwardList类的构成
//当前网页节点
@property (nullable, nonatomic, readonly, strong) WKBackForwardListItem *currentItem; //前进的一个网页节点
@property (nullable, nonatomic, readonly, strong) WKBackForwardListItem *backItem; //回退的一个网页节点
@property (nullable, nonatomic, readonly, strong) WKBackForwardListItem *forwardItem; //获取某个index的网页节点
- (nullable WKBackForwardListItem *)itemAtIndex:(NSInteger)index; //获取回退的节点数组
@property (nonatomic, readonly, copy) NSArray<WKBackForwardListItem *> *backList; //获取前进的节点数组
@property (nonatomic, readonly, copy) NSArray<WKBackForwardListItem *> *forwardList;
4、WKWebView中,Native与JavaScript交互如下几种,这里列一下基本实现方式,如何实现请看范例
// 第一种:JavaScript调用Native,采用WKUserContentController方式注册,在WKScriptMessageHandler代理方法中实现 // 第二种:Native调用JavaScript,采用evaluteJavaScript:complementionHandler:方法直接调用JavaScript脚本中的函数来实现 // 第三种:将自定义的JavaScript代码在端上采用addUserScript方式注入,然后再用evaluteJavaScript:complementionHandler:方法实现 // 第四种:通过WKUIDelegate来处理交互时来实现
5、现在来看一下在实例化WKWebView的过程中,开发者可以都设置哪些配置。
5-1 创建配置
//创建配置config
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.suppressesIncrementalRendering = NO;
config.applicationNameForUserAgent = @"Safari";
5-2 设置进程池
//设置进程池,拥有同一个pool进程池的多个webView可以共享数据,如Cookie、用户凭证等信息
WKProcessPool *pool = [[WKProcessPool alloc] init];
config.processPool = pool;
4-2 设置偏好
//设置偏好,可以设置一些页面信息,如字体、是否支持js交互、是否允许自动打开js窗体
WKPreferences *preference = [[WKPreferences alloc] init];
preference.minimumFontSize = ;
preference.javaScriptEnabled = YES;
preference.javaScriptCanOpenWindowsAutomatically = YES;
config.preferences = preference;
5-3 设置内容交互控制器,处理JavaScript与Native交互
//创建内容交互控制器,处理js与native的交互
//使用addScriptMessageHandler:name: 注册JavaScript要调用的方法名称,设置处理代理并且注册要被JavaScript调用的方法名称 name:方法名称
//使用addUserScript:注入代码,用window.webkit.messageHandlers.name.postMessage()向Native发送消息,支持OC中字典、数组、NSNumber等,设置Cookie
//例如注入cookie代码:NSString *cookieSource= @"document.cookie = 'token=12344';document.cookie = 'userName=xyq';";
//例如注入函数代码:NSString *funcSource = @"function func(){}";
//注意:要想使用方式二,方式一这一步不可省略。方式一实现后,可以根据情况选择是否需要使用方式二。
WKUserContentController *userContentController = [[WKUserContentController alloc] init]; //方式一:注册函数,代理回调,这个在H5文件内使用了 "window.webkit.messageHandlers.methodInvoke.postMessage()"
[userContentController addScriptMessageHandler:self name:methodInvoke]; //方式二:注入代码,直接调用,手动注入代码实现了"window.webkit.messageHandlers.methodInvoke.postMessage()"
NSString *methodSource = @"function print(){ window.webkit.messageHandlers.methodInvoke.postMessage(\"hello js!\")}";
WKUserScript *userScript = [[WKUserScript alloc] initWithSource:methodSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
[userContentController addUserScript:userScript]; //设置内容交互控制器
config.userContentController = userContentController;
5-4 设置数据存储
//设置数据存储,单例
//defaultDataStore:默认的存储器,会将数据写入磁盘
//nonPersistentDataStore:临时的存储器
WKWebsiteDataStore *store = [WKWebsiteDataStore nonPersistentDataStore];
config.websiteDataStore = store;
5-5 设置代理
//创建WKWebView
self.wkWebView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; //设置导航代理
self.wkWebView.navigationDelegate = self; //设置js弹出框代理
self.wkWebView.UIDelegate = self;
5-6 加载资源webkit.html
<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title> <style>
.divcss{ border:1px solid #F00; width:300px; height:200px}
</style> <script type="text/javascript">
function showAlert(){
alert("我被成功调起来了");
} function disp_confirm()
{
var r=confirm("Press a button")
if (r==true){
document.write("You pressed OK!")
}
else{
document.write("You pressed Cancel!")
}
} function disp_prompt()
{
var name=prompt("Please enter your name","")
if (name!=null && name!=""){
document.write("Hello " + name + "!")
}
} </script> </head> <body> <div class="divcss">
<!-- 这是一个链接跳转地址,点击时,端上将会进行拦截,弹出日志 -->
<a href="http://www.w3school.com.cn/">Visit W3School</a>
<br>
<br>
<a href="parent://www.parent.com.cn/">open parent</a>
</div> <input type="button" onclick="disp_confirm()"
value="Display a confirm box" /> <input type="button" onclick="disp_prompt()"
value="Display a prompt box" /> </body> </html>
//加载资源
NSString *file = [[NSBundle mainBundle] pathForResource:@"webkit" ofType:@"html"];
NSString *html = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
[self.wkWebView loadHTMLString:html baseURL:nil];
[self.view addSubview:self.wkWebView];
//释放资源
-(void)dealloc {
//必须移除注入的JS代码
//否则wkWebView无法被释放
[self.wkWebView.configuration.userContentController removeScriptMessageHandlerForName:methodInvoke];
//[self.wkWebView.configuration.userContentController removeAllUserScripts];
}
6、WKWebView的代理方法
6-1 接收注册JavaScript函数的代理回调<WKScriptMessageHandler>
#pragma mark - WKScriptMessageHandler
// 获取调用JavaScript代码后传递过来的消息, 也即接收通过 window.webkit.messageHandlers.methodInvoke.postMessage()传递过来的数据
// WKScriptMessage:JavaScript传递过来的消息实体,包含:消息主体body、网页视图webView、网页视图页面对象frameInfo、方法名称name
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { NSLog(@"message.body:-------%@------",message.body);
}
6-2 监听网页加载视图导航和页面渲染的代理回调<WKNavigationDelegate>
#pragma mark - WKNavigationDelegate //---------------------- 控制跳转 注意:方法1和方法2根据自己的需要只重写一个即可,系统只会调用一个 ----------------------- /* navigationType
* WKNavigationTypeLinkActivated,
* WKNavigationTypeFormSubmitted,
* WKNavigationTypeBackForward,
* WKNavigationTypeReload,
* WKNavigationTypeFormResubmitted,
* WKNavigationTypeOther = -1,
*/ // 在发送请求时,决定是否跳转
// decisionHandler:决定是否响应网页的某一个行为,包括加载、回退、刷新等
// WKNavigationActionPolicyCancel:取消行动
// WKNavigationActionPolicyAllow: 执行行动
// 在此代理方法可以设置拦截事件,实现Native与h5交互
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { NSString *scheme = navigationAction.request.URL.scheme;
NSLog(@"1-scheme:-------%@-----",scheme); // 在这儿添加端上需要执行的代码
if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
if ([scheme isEqual:@"parent"]) { NSString *absoluteString = navigationAction.request.URL.absoluteString; UIAlertController *aletVc = [UIAlertController alertControllerWithTitle:@"请求时拦截" message:absoluteString preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[aletVc addAction:action];
[self presentViewController:aletVc animated:YES completion:nil]; decisionHandler(WKNavigationActionPolicyCancel); return;
}
} decisionHandler(WKNavigationActionPolicyAllow); } // 在发送请求时,决定是否跳转
// decisionHandler:决定是否响应网页的某一个行为,包括加载、回退、刷新等
// WKNavigationActionPolicyCancel:取消行动
// WKNavigationActionPolicyAllow: 执行行动
// 在此代理方法也可以设置拦截事件,实现Native与h5交互
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction preferences:(WKWebpagePreferences *)preferences decisionHandler:(void (^)(WKNavigationActionPolicy, WKWebpagePreferences *))decisionHandler { NSString *scheme = navigationAction.request.URL.scheme;
NSLog(@"2-scheme:-------%@-----",scheme); // 在这儿添加端上需要执行的代码
if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
if ([scheme isEqual:@"parent"]) { NSString *absoluteString = navigationAction.request.URL.absoluteString; UIAlertController *aletVc = [UIAlertController alertControllerWithTitle:@"请求时拦截" message:absoluteString preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[aletVc addAction:action];
[self presentViewController:aletVc animated:YES completion:nil]; decisionHandler(WKNavigationActionPolicyCancel,preferences); return;
}
} decisionHandler(WKNavigationActionPolicyAllow,preferences);
} // 在收到响应后,决定是否跳转
// decisionHandler:决定是否响应网页的某一个行为,包括加载、回退、刷新等
// WKNavigationResponsePolicyCancel:取消响应
// WKNavigationResponsePolicyAllow:允许响应
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler { NSString *scheme = navigationResponse.response.URL.scheme;
NSLog(@"3-scheme:-------%@-----",scheme); // 在这儿添加端上需要执行的代码
if ([scheme isEqualToString:@"https"] || [scheme isEqualToString:@"htpps:"]) { NSString *absoluteString = navigationResponse.response.URL.absoluteString; UIAlertController *aletVc = [UIAlertController alertControllerWithTitle:@"响应后拦截" message:absoluteString preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[aletVc addAction:action];
[self presentViewController:aletVc animated:YES completion:nil];
} decisionHandler(WKNavigationResponsePolicyAllow);
} //-------------------------------------------- 监听流程 -------------------------------------------
// 当页面加载启动时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
NSLog(@"1---------当页面加载启动时调用---------");
} // 当主机接收到的服务发生重定向时调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
NSLog(@"2---------当主机接收到的服务发生重定向时调用---------");
} // 当主页数据加载发生错误时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
NSLog(@"3---------当主页数据加载发生错误时调用---------");
} // 当内容到达主机时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {
NSLog(@"4---------当内容到达主机时调用---------");
} // 当主页加载完成时调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
NSLog(@"5---------当主页加载完成时调用---------"); //Native调用JavaScript代码
//调用H5页面中定义的showAlert函数
[webView evaluateJavaScript:@"showAlert()" completionHandler:^(id _Nullable obj, NSError * _Nullable error) {
if (error) {
NSLog(@"call happend error----%@",error);
}
}]; //Native调用JavaScript代码
//调用端上注入的js代码中的print函数
[webView evaluateJavaScript:@"print()" completionHandler:^(id _Nullable obj, NSError * _Nullable error) {
if (error) {
NSLog(@"call happend error----%@",error);
}
}];
} // 当提交发生错误时调用
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
NSLog(@"6---------当提交发生错误时调用---------");
} // 当需要验证身份凭据时调用
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler { //身份验证凭据
NSURLCredential * credential = [[NSURLCredential alloc] initWithTrust:[challenge protectionSpace].serverTrust]; //为challenge的发送方提供credential
[[challenge sender] useCredential:credential forAuthenticationChallenge:challenge]; completionHandler(NSURLSessionAuthChallengeUseCredential,credential); NSLog(@"7---------验证身份凭据时调用---------");
} // 当进程被终止时调用
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView { NSLog(@"8---------当进程被终止时调用---------");
}
6-3 监听JavaScript的交互过程的回调,如alert等<WKUIDelegate>
#pragma mark - WKUIDelegate // 当创建新的webView时,会调用该代理方法
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
NSLog(@"--------打开新的webView------");
return webView;
} // 当调用关闭webView时,会调用该代理方法
- (void)webViewDidClose:(WKWebView *)webView {
NSLog(@"--------关闭打开的webView------");
} // ----------------------------- 下面的这些方法是交互JavaScript的方法 --------------------------
// JavaScript调用alert方法后回调的方法,message中为alert提示信息,必须要在其中调用completionHandler
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler { NSLog(@"---------alert message: %@---------",message);
//显示alert信息
UIAlertController *aletVc = [UIAlertController alertControllerWithTitle:@"alert" message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[aletVc addAction:action];
[self presentViewController:aletVc animated:YES completion:nil]; completionHandler(); } // JavaScript调用Confirm提交表单方法后的回调的方法,Confirm是JavaScript的确定框,需要在block中把用户选择的情况传递出去
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler { NSLog(@"---------comfirm message: %@---------",message);
//显示确认信息
UIAlertController *aletVc = [UIAlertController alertControllerWithTitle:@"comfirm" message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[aletVc addAction:action];
[self presentViewController:aletVc animated:YES completion:nil]; completionHandler(YES);
} // JavaScript调用Prompt方法后的回调的方法,Prompt是JavaScript的输入框,需要在block中把用户输入的信息传递出去
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler { NSLog(@"---------prompt message: %@---------",prompt);
//显示输入框信息
UIAlertController *aletVc = [UIAlertController alertControllerWithTitle:@"prompt" message:prompt preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[aletVc addAction:action];
[self presentViewController:aletVc animated:YES completion:nil]; completionHandler(prompt);
}
6-4 测试打印信息
//启动后
-- ::52.885769+ Webkit[:] -scheme:-------about-----
-- ::52.901501+ Webkit[:] ---------当页面加载启动时调用---------
-- ::52.910799+ Webkit[:] ---------当内容到达主机时调用---------
-- ::52.913347+ Webkit[:] ---------当主页加载完成时调用---------
-- ::52.916248+ Webkit[:] ---------alert message: 我被成功调起来了---------
-- ::47.352925+ Webkit[:] message.body:-------{
list = (
,
, );
name = XYQ;
qq = ;
}------
-- ::35.903852+ Webkit[:] message.body:-------hello js!------ //点击第一个链接
-- ::47.623126+ Webkit[:] -scheme:-------http-----
-- ::47.644042+ Webkit[:] ---------当页面加载启动时调用---------
-- ::47.754294+ Webkit[:] -scheme:-------https-----
-- ::47.757087+ Webkit[:] ---------当主机接收到的服务发生重定向时调用---------
-- ::47.853544+ Webkit[:] ---------验证身份凭据时调用---------
-- ::47.949375+ Webkit[:] -scheme:-------https-----
-- ::47.990267+ Webkit[:] ---------当内容到达主机时调用---------
-- ::48.139897+ Webkit[:] ---------验证身份凭据时调用---------
-- ::48.535383+ Webkit[:] ---------验证身份凭据时调用---------
-- ::48.936130+ Webkit[:] ---------验证身份凭据时调用---------
-- ::49.023373+ Webkit[:] ---------当主页加载完成时调用--------- //点击第二个链接
-- ::31.248867+ Webkit[:] -scheme:-------parent----- //点击Confirm确认框
-- ::09.871249+ Webkit[:] ---------comfirm message: Press a button--------- //点击Prompt输入框
-- ::35.748476+ Webkit[:] ---------prompt message: Please enter your name---------
Hybrid App: 对比UIWebView和WebKit实现JavaScript与Native交互的更多相关文章
- Hybrid App: 看看第三方WebViewJavascriptBridge是如何来实现Native和JavaScript交互
一.简介 在前面两篇文章中已经介绍了Native与JavaScript交互的几种方式,依次是JavaScriptCore框架.UI组件UIWebView.WebKit框架,这几种方式都是苹果公司提供的 ...
- Hybrid App 开发模式
开发移动App主要有三种模式:Native. Hybrid 和 Web App. 需要注意的一点是在选择开发模式的时候,要根据你的项目类型(图片类?视频类?新闻类?等),产品业务和人员技术储备等做权衡 ...
- Hybrid App 开发初探:使用 WebView 装载页面
Hybrid App 是混合模式应用的简称,兼具 Native App 和 Web App 两种模式应用的优势,开发成本低,拥有 Web 技术跨平台特性.目前大家所知道的基于中间件的移动开发框架都是采 ...
- Hybrid App简介
Hybrid App 是混合模式应用的简称,兼具 Native App 和 Web App 两种模式应用的优势,开发成本低,拥有Web技术跨平台特性.目前大家所知道的基于中间件的移动开发框架都是采用的 ...
- Hybrid App—Hybrid App开发模式介绍和各种开发模式对比
什么是Hybrid App 最开的App开发只有原生开发这个概念,但自从H5广泛流行后,一种效率更高的开发模式Hybrid应运而生,它就是"Hybrid模式".Hybrid APP ...
- Native App, Hybrid App, Web App对比
Native App,Hybrid App和Web App简介 目前基本所有的移动互联网app可以分为三类:Native App,Hybrid App和Web App. Native App是基于智能 ...
- Hybrid App: 了解JavaScript如何与Native实现混合开发
一.简介 Hybrid Development混合开发是目前移动端开发异常火热的新兴技术,它能够实现跨平台开发,极大地节约了人力和资源成本.跨平台开发催生了很多新的开源框架,就目前而言,在混合开发中比 ...
- Native APP ,Web APP,Hybrid APP三者对比
Native APP Native APP 指的是原生程序(Android.iOS.WP),一般依托于操作系统,有很强的交互,可拓展性强,需要用户下载安装使用,是一个完整的App. 原生应用程序是某一 ...
- Hybrid App开发模式中, IOS/Android 和 JavaScript相互调用方式
IOS:Objective-C 和 JavaScript 的相互调用 iOS7以前,iOS SDK 并没有原生提供 js 调用 native 代码的 API.但是 UIWebView 的一个 dele ...
随机推荐
- 入职小白随笔之Android四大组件——广播详解(broadcast)
Broadcast 广播机制简介 Android中的广播主要可以分为两种类型:标准广播和有序广播. 标准广播:是一种完全异步执行的广播,在广播发出之后,所有的广播接收器几乎都会在同一时刻接收到这条广播 ...
- Python生成requirements.txt方法
在查看别人的Python项目时,经常会看到一个requirements.txt文件,里面记录了当前程序的所有依赖包及版本号,其作用是用来在另一个环境上重新构建项目所需要的运行环境依赖. require ...
- 十一、Powell算法(鲍威尔算法)原理以及实现
一.介绍 Powell算法是图像配准里面的常用的加速算法,可以加快搜索速度,而且对于低维函数的效果很好,所以本篇博客主要是为了介绍Powell算法的原理以及实现. 由于网上已经有了对于Powell算法 ...
- JavaScript-----12.对象
1. 对象 万物皆对象,但是对象必须是一个具体的事物.例如:"明星"不是对象,"周星驰"是对象:"苹果"不是对象"这个苹果&quo ...
- git 进行版本打标签
一般给生产环境的代码新包进行打标签,以便查找,发布正式环境的各个不同版本作用,简单来说,就是给包命名,容易区分太多版本啦 获取系统中的所有标签或筛选特定特征的标签 git tag -a tagname ...
- 用dotnet core搭建web服务器(三)ORM访问数据库
访问传统sql数据库,大家以前都是用sql语句去查询.这些年流行orm方法 ORM是对象关系映射的简拼,就是用一个对象(class)去表示数据的一行,用对象的成员去表述数据的列 dotnet 官方很早 ...
- 由异常:Repeated column in mapping for entity/should be mapped with insert="false" update="false 引发对jpa关联的思考
由异常:Repeated column in mapping for entity/should be mapped with insert="false" update=&quo ...
- Repeater嵌套
我们自己观察 这是由两个重复项组成的 重复项包含重复项 而重复项的数据源是由订单号决定 即父Repeater的某数据源字段 protected void Repeater1_ItemDataBound ...
- Winform中怎样在工具类中对窗体中多个控件进行操作(赋值)
场景 需求是在窗体加载完成后掉用工具类的方法,工具类中获取窗体的多个控件对象进行赋值. 注: 博客主页: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 ...
- mac pro下iterm快捷键(转)
标签 新建标签:command + t 关闭标签:command + w 切换标签:command + 数字 command + 左右方向键 切换全屏:command + enter 查找:comma ...