有时候咱们在内嵌的webview中但愿点击一个连接以后,触发iOS原生事件,而不是webview内页面跳转(由于webview的跳转很生硬,而ajax+js模拟则不如原生segue平滑)。html
有时候咱们但愿在页面内consloe.log('log something')
的时候在控制台里看到输出,但手机里没有控制台,因此咱们但愿能够利用xcode的控制台输出信息。ios
由于iOS没有提供API让咱们直接用html或者js来跟外部交互,因此咱们必须用另一种巧妙的办法来实现这两个功能。这种方法能够知足咱们两种需求。web
在html页面中从新定义console.log
:ajax
<script> // Debug console = new Object(); console.log = function(log) { var iframe = document.createElement("IFRAME"); iframe.setAttribute("src", "ios-log:#iOS#" + log); document.documentElement.appendChild(iframe); iframe.parentNode.removeChild(iframe); iframe = null; } console.debug = console.log; console.info = console.log; console.warn = console.log; console.error = console.log; </script>
而后在须要捕获的viewController.m中实现协议:objective-c
- (BOOL)webView: (UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ NSString *requestString = [[[request URL] absoluteString] stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding]; //NSLog(requestString); if ([requestString hasPrefix:@"ios-log:"]) { NSString* logString = [[requestString componentsSeparatedByString:@":#iOS#"] objectAtIndex:1]; NSLog(@"UIWebView console: %@", logString); return NO; } return YES; }
固然前提是webView须要把委托设定为当前控制器:json
self.webView =[[UIWebView alloc] initWithFrame:CGRectMake(0.0f,0.0f,self.view.bounds.size.width,self.view.bounds.size.height-44)]; self.webView.delegate=self;
原理很简单,咱们从新定义了console.log
函数,还有console.debug
,console.info
,console.warn
,console.error
。当咱们在页面js中调用console.log
的时候,就会建立一个iframe发出请求,请求的协议为ios-log:
,路径就是咱们的log字符串。发出请求以后,迅速把这个iframe清理掉。xcode
这样,在webview中咱们发出了一个请求,而后就没了。外部咱们用objective-c实现了一个协议,就是webview开始发出请求以前就会调用的函数。在这个函数中咱们过滤全部的请求(由于除了ios-log
,还有一些“正常”的请求好比http和mailto),当前缀为ios-log
的时候,咱们就NSLog
便可。app
if
最后的return NO
的意思是该webview的请求被捕获,再也不请求(这个实际上不存在的页面)。咱们但愿一些合法请求(好比http、mailto等)不被捕获,因此最后if
外面丢了一个return YES
。编辑器
webview中的代码:ide
<a href="myapp://somepagename">一个按钮</a>
很是简单,myapp这个协议你能够本身随便命名,稍后咱们会在objective-c中捕获它。
仍是要实现该webview的委托controller的协议方法,若是你已经定义这个方法了(就像上面那个例子),你只须要在方法体里加入方法体里面的内容,不然会提醒你重复定义。
- (BOOL)webView: (UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ NSURL *url = [request URL]; if ( [[url scheme] isEqualToString:@"myapp"] ) { NSString *slug = [url path]; [self performSegueWithIdentifier:@"heroSegue" sender:slug]; return NO; } return YES; }
另外我在interface builder中已经拖拽了一个新的控制器,在新的控制器跟导航控制器之间,我直接拖了一个segue,命名id为heroSegue
,因此这里能够用performSegueWithIdentifier
来调用segue。
如今,仍是在本controller中,咱们实现另外一个委托方法:
/* * 页面转换时触发 */ - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:@"heroSegue"]) { NSLog(@"%@",sender); [self.webView stopLoading]; YUGHeroDetailViewController *destViewController = segue.destinationViewController; destViewController.heroSlug = (NSString *)sender; } }
也就是说,发生segue变化以前,就会执行这一方法,首先判断identifier是否是等于heroSegue,若是是,本身的webview再也不载入,目标控制器(也就是即将切换过去的子页面的控制器)中设置公有属性heroSlug的值。
而后,咱们在目标页面的controller的H中定义:
@property (strong) NSString *heroSlug;
最后,在目标页面中,咱们定义的congroller中的M能拿到heroSlug这个参数。
NSLog(@"%@",self.heroSlug);
这样就能够了,拿到slug以后,咱们实际上就能够调用一个本地页面,带上slug参数,而后经过ajax的方式读取远程页面或者json数据,这个就不属于本文内容了。
若是你是新手,在作上面的这些操做的时候可能会漏掉一两个步骤,编辑器会报错,这时候仔细阅读并校对你的代码。若是实在不行,说明清楚操做和报错信息,再给我留言。
练习题:原生title的好处是它在字符数较短时是居中的,而字符更长一点时会偏右显示,更长一些时显示省略号。那么webview载入一个ajax数据的页面的时候,如何在页面载入成功时,设置原生title?
提示,仍是自定义协议。