ios原生态页面相对于HTML5页面具有渲染效果好,流畅,省流量的特点,但是比较难实现动态加载多个页面(如支付宝),动态修改页面布局。
我们使用的是WebViewJavascriptBridge这个第三方控件。
html5有四种加载方式。
第一种,在h5页面加入工程,本地加载方式。
第二种,判断本地是否有h5页面文件,若没有向服务发送文件请求,下载h5页面,然后本地加载。
第三种,不经过服务器以加载页面的方式加载页面,它是一种同步加载方式。
第四种,向服务器发送请求获得html5页面的加载方式,它是一种异步加载方式。
通过下面的方法加载本地HTML5页面。若本地没有HTML5页面文件,通过向服务器发送请求接收HTML5页面文件,让后加载本地HTML5页面。缺点不能实时更新页面,服务器变更不能在本地体现,不能统计页面点击量,不能识别是谁点击的页面,好处是省流量,一次下载一劳永逸。
NSString* htmlPath = [[NSBundle mainBundle] pathForResource:html ofType:@"html"]; NSString* appHtml = [NSString stringWithContentsOfFile:htmlPath encoding:NSUTF8StringEncoding error:nil]; NSURL *baseURL = [NSURL fileURLWithPath:html]; [webView loadHTMLString:appHtml baseURL:baseURL];
第三种方案,实现很简单,估计很多app都是用这种方法,但是这种方案难以对各种异常进行友好提示,并且是同步加载方式,堵塞整个流程。典型代码实现:
NSURLRequest* request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0]; [self.webView loadRequest:request];
下面都是采用第四种实现方案,这个是HTML5的源代码,bridge.callHandler(‘endMyDegreeIOS’,data, function(responseData) {});这个代码中endMyDegreeIOS是调用的IOS的函数。通过这样的代码可以实现HTML5调用IOS的代码。它的本质
是通过回调函数找到IOS的函数的。
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <script type="text/javascript" src="http://172.16.28.165:8080/fhl/statics/easyui/jquery-1.4.4.min.js"></script> <body> <script> /* * IOS下 连接HTML的代码 */ function connectWebViewJavascriptBridge(callback) { if (window.WebViewJavascriptBridge) { callback(WebViewJavascriptBridge) } else { document.addEventListener('WebViewJavascriptBridgeReady', function() { callback(WebViewJavascriptBridge) }, false) } } connectWebViewJavascriptBridge (function(bridge) { // init方法是把数据传给开发 // 再调用responseCallback(data) bridge.init(function(message, responseCallback) { var data = { 'Javascript Responds 111111111':'Wee!' }; // 必须是json格式的 responseCallback(data) }); var data = {"message":"找不到运费信息"}; bridge.callHandler('endMyDegreeIOS',data, function(responseData) {}); }); </script> </body> </html>
下面是一个完整的调用HTML5页面。
_bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView webViewDelegate:self handler:^(id data, WVJBResponseCallback responseCallback) 这个是HTML5页面回调用的,没有这个一切免谈了,一切异常都处理不了,HTML5根本找不到ios的函数,只能处理正常加载成功的情况。
同过[webView loadRequest:request];可以向服务器发送请求HTML5页面。
NSURLRequest *request = [User getBookPageRequest];这个设置向服务发送的请求,关键是拼接 http参数字符串。[webView loadRequest:request];这个就是加载服务器返回的html5页面。
// // InstructionViewController.m // FHL // // Created by panume on 14-10-21. // Copyright (c) 2014年 JUEWEI. All rights reserved. // #import "InstructionViewController.h" #import "WebViewJavascriptBridge.h" #import "SecondWebViewController.h" #import "LoginViewController.h" #import "AnimatedView.h" #import "GlobalVariable.h" #import "Reachability.h" @interface InstructionViewController () <UIWebViewDelegate> @property (nonatomic, strong) WebViewJavascriptBridge *bridge; @property (nonatomic, strong) UIWebView *webView; @property (nonatomic, strong) AnimatedView *animatedView; @property (nonatomic, strong) UIActivityIndicatorView *indicatorView; @end @implementation InstructionViewController - (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]; if (self) { } return self; } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view from its nib. self.animatedView = [[AnimatedView alloc] initWithFrame:CGRectMake(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT) imageName:@"logo_watermark_run" remark:@"正在努力加载中"]; __weak __typeof(self)weakSelf = self; self.animatedView.touchAction = ^(void){ [weakSelf loadExamplePage:weakSelf.webView]; }; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; self.view.backgroundColor = [UIColor colorWithHex:0xf5f5f5]; if (!self.webView) { self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds]; self.webView.backgroundColor = [UIColor whiteColor]; [self.view addSubview:self.webView]; if (!self.indicatorView) { self.indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; self.indicatorView.frame = CGRectMake(0, 0, 40, 40); self.indicatorView.center = self.webView.center; self.indicatorView.hidesWhenStopped = YES; [self.view addSubview:self.indicatorView]; [self.indicatorView startAnimating]; } [WebViewJavascriptBridge enableLogging]; _bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView webViewDelegate:self handler:^(id data, WVJBResponseCallback responseCallback) { FLDDLogDebug(@"ObjC received message from JS: %@", data); responseCallback(@"Response for message from ObjC"); }]; [_bridge registerHandler:@"reloadPage" handler:^(id data, WVJBResponseCallback responseCallback) { FLDDLogDebug(@"reloadPage"); [self reLoginReloadPage]; }]; [_bridge registerHandler:@"fhlSecondCallback" handler:^(id data, WVJBResponseCallback responseCallback) { FLDDLogDebug(@"testObjcCallback called: %@", data); SecondWebViewController *secondWebVC = [[SecondWebViewController alloc] init]; NSString *html = [data toString]; if ([html isEqualToString:@"shouce"]) { secondWebVC.title = @"配送手册"; secondWebVC.type = SWebViewTypeNotebook; } else if ([html isEqualToString:@"learn"]) { secondWebVC.title = @"在线培训"; secondWebVC.type = SWebViewTypeLearn; } [self.navigationController pushViewController:secondWebVC animated:YES]; responseCallback(@"Response from testObjcCallback"); }]; [_bridge send:@"123"]; [self loadExamplePage:self.webView]; } } - (void)loadExamplePage:(UIWebView*)webView { if (self.animatedView.superview) { [self.animatedView removeFromSuperview]; self.animatedView.hidden = NO; if (self.indicatorView.superview) { self.indicatorView.hidden = NO; self.indicatorView.center = self.webView.center; [self.view bringSubviewToFront:self.indicatorView]; [self.indicatorView startAnimating]; } } NSURLRequest *request = [User getBookPageRequest]; if(request != nil) { [webView loadRequest:request]; } } -(void)reLoginReloadPage { [[UIApplication sharedApplication] unregisterForRemoteNotifications];//用户退出登录后,取消推送的注册,登录时register if ([AppManager boolValueForKey:@"isFirstReLogin"]) { [AppManager setUserBoolValue:NO key:@"isFirstReLogin"]; [self needLoginIn]; } } - (void)needLoginIn { [[User currentUser] removeUserInfo]; NSString *phone = [AppManager valueForKey:@"telephone"]; NSString *password = [AppManager valueForKey:@"password"]; if (phone == nil || password == nil) { LoginViewController *loginViewController = [[LoginViewController alloc] init]; // UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:loginViewController]; // navigationController.navigationBar.barTintColor = [UIColor colorWithHex:0xf5f5f5]; // [self.navigationController presentViewController:navigationController animated:YES completion:nil]; [self.navigationController pushViewController:loginViewController animated:YES]; return; } BOOL isRegister = [AppManager boolValueForKey:@"isRegister"]; if (isRegister) { AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate; for (UIViewController *viewController in delegate.navigationController.viewControllers) { if ([viewController isKindOfClass:[LoginViewController class]]) { [delegate.navigationController popToViewController:viewController animated:YES]; return; } } } NSDictionary *params = @{@"courierTel": phone, @"passwd" : password}; [User reLoginWithParams:params block:^(NSArray *districts ,NSError *error) { if (error) { NSString *alert = @"账户在其他设备登录"; UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:nil message:alert delegate:self cancelButtonTitle:@"确定" otherButtonTitles:nil, nil]; alertView.tag = 1007; [alertView show]; } else { // [[NSNotificationCenter defaultCenter] postNotificationName:HIT_HEART_NOTIFICATION object:nil userInfo:@{@"beginHit" : @(YES)}]; // [[NSNotificationCenter defaultCenter] postNotificationName:UPDATE_LOCATION_NOTIFICATION object:nil]; [[NSNotificationCenter defaultCenter] postNotificationName:PUSH_CONFIG_NOTIFICATION object:nil]; [[NSNotificationCenter defaultCenter] postNotificationName:GET_MESSAGE_COUNT object:nil]; [AppDelegate registerForRemoteNotification]; [AppManager setUserBoolValue:YES key:@"isFirstReLogin"]; NSURLRequest *request = [User getBookPageRequest]; if(request != nil) { [self.webView loadRequest:request]; } } }]; } - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if (alertView.tag == 1007) { if (buttonIndex == alertView.cancelButtonIndex) { [[UIApplication sharedApplication] unregisterForRemoteNotifications]; [AppManager setUserBoolValue:YES key:@"NeedRefreshMes"]; // [AppManager setUserBoolValue:YES key:@"NeedRefreshStage"]; [AppManager setUserBoolValue:YES key:@"NeedRefreshHome"]; [AppManager setUserBoolValue:NO key:@"HasShowHeader"]; AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate; delegate.tabBarController.selectedIndex = 0; [delegate.tabBarController.navigationController popToRootViewControllerAnimated:NO]; [AppManager setUserBoolValue:YES key:@"refreshCode"]; LoginViewController *loginViewController = [[LoginViewController alloc] init]; // UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:loginViewController]; // navigationController.navigationBar.barTintColor = [UIColor colorWithHex:0xf5f5f5]; // [delegate.tabBarController.navigationController presentViewController:navigationController animated:NO completion:nil]; [delegate.tabBarController.navigationController pushViewController:loginViewController animated:YES]; } } } #pragma mark - UIWebViewDelegate - (void)webViewDidStartLoad:(UIWebView *)webView { FLDDLogDebug(@"webViewDidStartLoad"); [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; } - (void)webViewDidFinishLoad:(UIWebView *)webView { [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; FLDDLogDebug(@"webViewDidFinishLoad"); [self.indicatorView stopAnimating]; [_bridge send:@"123"]; } - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; FLDDLogDebug(@"instruction wevView load error:%@", error); [self.indicatorView stopAnimating]; if (self.animatedView) { self.animatedView.animatedState = AnimatedViewStateFailed; [self.view addSubview:self.animatedView]; } if(NotReachable == g_networkStatus) { // [SVProgressHUD showErrorWithStatus:g_errorNoNetDes]; [MBProgressHUD hudShowWithStatus:self : g_errorNoNetDes]; } else { [MBProgressHUD hudShowWithStatus:self :[error localizedDescription]]; } } @end
这个是用charles截获的请求。
http://test.zuixiandao.cn/fhl/phone/psy/toBookIOSJsonPhone.htm?visitTime=1437363928&phoneId=ios_120e438f-9757-451d-b980-0d87824b02eb&key=9CC427BB979A9C0550F9BFFC2BDDD173&cmdCode=8100&cmdCode=8100
这个是用charles截获的请求响应。
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" /> <title>Document</title> <style> body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,pre,form,fieldset,legend,button,input,textarea,th,td{margin:0;padding:0;font-family:"Microsoft yahei";color:#000;} h1,h2,h3,h4,h5,h6{font-weight:500;} ul,ol,li{list-style:none;} i,em{font-style:normal} img{max-width:100%;} body{font-size:1em;background:#eee;} .book-container {width:100%;height:100%;background:#eee;overflow:hidden;} .book-header{height:100%;overflow:hidden;} .book-nav {width:100%;margin-top:-4px;overflow:hidden;} .book-content{width:100%;background:#fff;overflow:hidden;} .book-content ul{width:100%;margin:0 auto;overflow:hidden;} .book-content li {height:66px;line-height:66px;border-bottom:1px solid #e0e0e0;overflow:hidden;} .book-content li.last-col{border-bottom:none;} .book-content li a{display:block;height:66px;text-decoration:none;overflow:hidden;} .book-content li em{float:left;margin-left:12px;margin-top:11px;font-size:1.125em;height:44px;line-height:44px;color:#333333;} .book-content li .notebook-icon { float:left; margin-top:12px; background:url("http://test.zuixiandao.cn/fhl/templates/Html/notebook.png") no-repeat 0 5px; width:48px; height:48px; background-size:32px 32px; margin-left:26px; } .book-content li .online-icon { float:left; margin-top:12px; background:url("http://test.zuixiandao.cn/fhl/templates/Html/train_icon.png") no-repeat 0 5px; width:48px; height:48px; background-size:32px 32px; margin-left:26px; } .book-content .last-col i, .book-content .public-icon i{ float:right; padding-right:16px; margin-top:23px; background:url("http://test.zuixiandao.cn/fhl/templates/Html/jiantou.png") no-repeat 0px 0; width:15px; height:25px; background-size:12px 20px; } .more-content {width:100%;height:66px;line-height:66px;border-top:1px solid #d7d7d7;overflow:hidden;} .more-content i{float:left;height:66px;width:66px;margin-top:0px;background:url("http://test.zuixiandao.cn/fhl/templates/Html/more-icon.png") no-repeat;background-size:66px 66px;} .more-content span{float:left;margin-left:19px;font-size:1.125em;color:#f2764f;} </style> </head> <body> <div class="book-container"> <div class="book-header"> <img src="http://test.zuixiandao.cn/fhl/templates/Html/book-top.png" width="100%"/> </div> <div class="book-nav"> <div id="tab-contnetWrapper"> <div class="book-content"> <ul> <li class="public-icon"> <a id="online-train"> <span class="online-icon"></span> <em>在线培训</em> <i></i> </a> </li> <li class="last-col"> <a id="bookId"> <span class="notebook-icon"></span> <em>配送手册</em> <i></i> </a> </li> </ul> <div class="more-content"> <i></i><span>更多内容,敬请期待哦~</span> </div> </div> </div> </div> </div> <script> function connectWebViewJavascriptBridge(callback) { if (window.WebViewJavascriptBridge) { callback(WebViewJavascriptBridge) } else { document.addEventListener('WebViewJavascriptBridgeReady', function() { callback(WebViewJavascriptBridge) }, false) } } connectWebViewJavascriptBridge(function(bridge) { var uniqueId = 1 function log(message, data) { var log = document.getElementById('log') var el = document.createElement('div') el.className = 'logLine' el.innerHTML = uniqueId++ + '. ' + message + ':<br/>' + JSON.stringify(data) if (log.children.length) { log.insertBefore(el, log.children[0]) } else { log.appendChild(el) } } bridge.init(function(message, responseCallback) { var data = { 'Javascript Responds 111111111':'Wee!' } responseCallback(data) }) bridge.registerHandler('fhlFirstJavascriptHandler', function(data, responseCallback) { responseCallback(data) }); var bookId = document.getElementById("bookId"), trainId = document.getElementById("online-train"); bookId.addEventListener('touchstart',function(){ var data = 'shouce' bridge.callHandler('fhlSecondCallback',data, function(responseData) { }) }) trainId.addEventListener('touchstart',function(){ var data = 'learn' bridge.callHandler('fhlSecondCallback',data, function(responseData) { }) }) }) </script> </body> </html>