本文转载至 http://ju.outofmemory.cn/entry/18807

有时候我们在内嵌的webview中希望点击一个链接之后,触发iOS原生事件,而不是webview内页面跳转(因为webview的跳转很生硬,而ajax+js模拟则不如原生segue平滑)。

有时候我们希望在页面内consloe.log('log something')的时候在控制台里看到输出,但手机里没有控制台,所以我们希望可以利用xcode的控制台输出信息。

因为iOS没有提供API让我们直接用html或者js来跟外部交互,所以我们必须用另外一种巧妙的办法来实现这两个功能。这种方法可以满足我们两种需求。

console.log

在html页面中重新定义console.log:

<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中实现协议:

- (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需要把委托设定为当前控制器:

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清理掉。

这样,在webview中我们发出了一个请求,然后就没了。外部我们用objective-c实现了一个协议,就是webview开始发出请求之前就会调用的函数。在这个函数中我们过滤所有的请求(因为除了ios-log,还有一些“正常”的请求比如http和mailto),当前缀为ios-log的时候,我们就NSLog即可。

if最后的return NO的意思是该webview的请求被捕获,不再请求(这个实际上不存在的页面)。我们希望一些合法请求(比如http、mailto等)不被捕获,所以最后if外面丢了一个return YES

利用链接触发场景变换

iOS原生的场景变换叫做segue,xcode为我们内置了几种原生动画,比如导航条总是固定在上面的push,这样页面前后推动的时候,导航条保持不变(当然里面的内容可以变),内容的切换也很流畅。segue还可以在interface builder中设置动画效果,包括全屏翻转或者渐进等。

有一些第三方js库可以让我们在webview中模拟这种场景变换,也就是说用css设计一个导航条放在webview中置顶,然后用js或者css3来模拟push或者flip3d的效果。比如iScroll是模拟顶部和底部固定的,jQtouch(这个要翻墙搜索)是模拟push和flip3d效果的。

我强烈反对在原生应用中使用js库来实现场景变换动画,因为非常不流畅、不跟手指、动画效果跟原生不同,还有最后一个原因,我们是可以在webview中触发外部场景变换的,原生的!用html5制作流畅的原生app的关键就是能够方便地调用原生接口的功能或者效果我们都用原生的,而不去用笨拙的方法实现。

webview中的代码:

<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?

提示,还是自定义协议。

使用HTML5构建iOS原生APP(2)的更多相关文章

  1. Bodymovin:Bodymovin和Lottie:把AE动画转换成HTML5/Android/iOS原生动画

    转自:https://www.cnblogs.com/zamhown/p/6688369.html 大杀器Bodymovin和Lottie:把AE动画转换成HTML5/Android/iOS原生动画 ...

  2. 混合app开发,h5页面调用ios原生APP的接口

    混合APP开发中,前端开发H5页面,不免会把兼容性拉进来,在做页面的兼容性同事,会与原生app产生一些数据交互: 混合APP开发,安卓的兼容性倒是好说,安卓使用是chrome浏览器核心,已经很好兼容H ...

  3. 大杀器Bodymovin和Lottie:把AE动画转换成HTML5/Android/iOS原生动画

    前段时间听部门老大说,Airbnb出了个移动端的动画库Lottie,可以和一个名叫Bodymovin的AE插件结合起来,把在AE上做好的动画导出为json文件,然后以Android/iOS原生动画的形 ...

  4. 看逐浪CMS技术小哥做SVG动画(附使用Bodymovin和Lottie将Adobe After Effects(AE)程式转为 HTML5/Android/iOS原生的动画全过程-即AE转svg\canvas\html5动画)

      名词解解释 adobe After Effects AE:adobe After Effects,adobe公司的专业视频制作软件. Bodymovin插件预览 Bodymovin:是一个AE的插 ...

  5. iOS原生App与H5页面交互笔记

    文/MikeZhangpy(简书作者)原文链接:http://www.jianshu.com/p/4ed3e5ed99c6著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. 最近在做一个项 ...

  6. iOS原生APP与H5+JS交互////////////////////zzzz

    原生代码中直接加载页面 1.    具体案例 加载本地/网络HTML5作为功能介绍页 2.    代码示例 //本地 -(void)loadLocalPage:(UIWebView*)webView ...

  7. iOS原生APP和H5交互-delegate和第三方

    一.原生代码中直接加载页面(拦截) 1.    具体案例 加载本地/网络HTML5作为功能介绍页 2.    代码示例 //本地 -(void)loadLocalPage:(UIWebView*)we ...

  8. HTML5定稿:手机App将三年内消失,互联网世界的第二次大战

    HTML5与app以对立竞争的产品形态展现在大众视野.从去年开始又有一大批技术派或者创业者盯向html5领域,移动游戏的爆发和微信朋友圈等众多平台为HTML5导流,能不能颠覆,或许只是时间上的问题. ...

  9. The Best One iOS Contacts App

    The Best One iOS Contacts App iPhone Contacts App SwiftUI Awesome iOS Contacts App 一款高度还原华为通讯录 iOS A ...

随机推荐

  1. struts struts拦截器(过滤器)

    在struts中尽量避免自定义拦截器,因为大部分需要自己定义拦截器的时候,设计思路就不对了.大部分拦截器框架都有给你定义好了.而且如果在struts中定义拦截器相当于和这个框架绑定了,假如以后要扩展或 ...

  2. Lr_debug_message,Lr_output_message,Lr_error_message,Lrd_stmt,Lrd_fetch

    今天在群里,问了 Lr_debug_message,Lr_output_message,Lr_error_message,Lrd_stmt,Lrd_fetch.下 面我整理了下Lr_debug_mes ...

  3. jdbc调用 oracle 存储过程操作

    创建有参存储函数findEmpNameAndSal(编号),查询7902号员工的的姓名和月薪,[返回多个值,演示out的用法]当返回2个或多个值,必须使用out符号当返回1个值,就无需out符号 cr ...

  4. 百度地图和高德地图坐标系的互相转换 四种Sandcastle方法生成c#.net帮助类帮助文档 文档API生成神器SandCastle使用心得 ASP.NET Core

    百度地图和高德地图坐标系的互相转换   GPS.谷歌.百度.高德坐标相互转换 一.在进行地图开发过程中,我们一般能接触到以下三种类型的地图坐标系: 1.WGS-84原始坐标系,一般用国际GPS纪录仪记 ...

  5. Hibernate关联关系(二) Cascade级联

    1.cascade定义的是关系两端对象到对象的级联关系:而inverse定义的是关系和对象的级联关系. all : 所有情况下均进行关联操作.  none:所有情况下均不进行关联操作.这是默认值.  ...

  6. EHCache 在WEB项目的使用方法

    EHCache 是一个纯java的在进程中的缓存,它具有下面特性:高速,简单,为Hibernate2.1充当可插入的缓存,最小的依赖性,全面的文档和測试.官方站点http://ehcache.sour ...

  7. Linux 连续运行多条命令

    每条命令使用";"隔开.则不管前边的命令运行成功与否都会继续运行下一条命令 这里,有益将第二条命令中的echo多写了一个o.命令运行出错,但并不影响兴许命令的运行 能够这么想,假设 ...

  8. .Net MVC ViewBag

    ViewBag用来做视图的值绑定,我不清楚是不是这么称呼.之前看过安卓的值绑定做法,mvc直接动态类型,瞬间逼格满满 Controller: public class HomeController : ...

  9. Java创建多线程的三种方法

    Java多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没 ...

  10. tomcat能启动正常,但是输入localhost:8080不能登录

    怎么配置JDK和TOMCAT应该百度经验已经很好地解释了. tomcat启动成功了,但是  localhost:8080  登录不成功. 有一种可能,缺少http:// 输入: http://loca ...