iOS Webview 与 app交互

有时候咱们在内嵌的webview中但愿点击一个连接以后,触发iOS原生事件,而不是webview内页面跳转(由于webview的跳转很生硬,而ajax+js模拟则不如原生segue平滑)。html

有时候咱们但愿在页面内consloe.log('log something')的时候在控制台里看到输出,但手机里没有控制台,因此咱们但愿能够利用xcode的控制台输出信息。ios

由于iOS没有提供API让咱们直接用html或者js来跟外部交互,因此咱们必须用另一种巧妙的办法来实现这两个功能。这种方法能够知足咱们两种需求。web

console.log

在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.debugconsole.infoconsole.warnconsole.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?

提示,仍是自定义协议。

相关文章
相关标签/搜索