做为一位 iOS 开发人员,你应该已经敏感地发现,本身的工做涉及内容已经不止于 Native 的部分,由于 Hybrid App 和 ReactNative 等技术方案已经不只仅是概念,愈来愈多的公司开始着手本身的 Hybrid 方案以及 ReactNative 本地化工做。前端
介绍相关概念的优秀文章已经有许多,方案的实现原理你也应该已经或多或少有了一些理解。不了解也没有关系,在这篇文章里,我将用简书 iOS 客户端的有关特性,来探索一下 Hybrid 方案的技术细节。文章的目的是抛砖引玉,用一个具体的项目,你们很熟悉的简书客户端,来帮助你们认识 Hybrid 方案,而后亲自实现它 。git
从如今开始,再也不着眼于某一个 feature ,你须要站在一个客户端架构师的角度来看待问题。github
webview
控件,具体是UIWebview
仍是WKWebview
按下不表,这不是本文的关键。在个人demo中,我使用了UIWebview
。发现
tab栏的内容顶部,还有一个热门内容推荐的轮播图。与它相似是一些app内的活动推介轮播图,以及广告页面,它们的详情页内容展现多使用webview。在简书中,这个轮播图对应的下一级页面也是文章展现页面,特性基本一致。返回
和关闭
按钮。以及右侧的功能列表按钮。评论
按钮(原生组件),页面(web页面)会滑动到评论区域,如图www.jianshu.com
下的内容,在加载过程当中,是没有进度条的,用户体验很是接近原生页面。而第三方的内容,则在加载过程当中会出现通常浏览器中常见的加载进度条,如图:关闭
按钮,这也是为了营造接近原生页面的用户体验,让用户不会察觉到这是一个 web 界面。而第三方内容,则会出现符合浏览器使用习惯的关闭
按钮,如上图。JavaScript
语句,交由webview进行执行,从而在web页面上实现须要的效果。而在web页面的js文件中,也能够调用原生的Objective-C
方法,从而执行一些原生方法才能完成的操做。与此相关的库有WebViewJavascriptBridge以及JavaScriptCore
,有须要的同窗能够自行了解。- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
UIWebview
拥有布尔类型的canGoBack
、loading
等属性,经过监测它们的值咱们能够知道当前页面是否能够进行回退,以及页面是否正在加载。- (void)goBack;
等方法,调用之页面会进行返回,就像咱们在浏览器中常见的那样。对于上文中提到的相关特性,我出写了一个demo,对它们进行了简要的模仿实现。固然简书官方的实现会考虑到方方面面,而个人demo仅是从Hybrid架构的思想出发,盼可以抛砖引玉。
这是demo中对该页面的模仿实现:
web
在demo中,使用一条web页面的URL来初始化VC:- (instancetype)initWithURL:(NSURL *)URL;
这条URL对应文章的连接。
顶部导航栏和底部工具栏都是系统原生的UINavigationBar
和UIToolBar
,按钮素材使用阿里巴巴的iconfont字体。json
关于这个特性的实现,若是按照 Hybrid 架构的思想,属于 Web 页面调用原生方法,进入一个原生的VC。点击头像,JS脚本执行相关代码,调用原生方法暴露出来的接口,执行原生方法。
我在这里用一种简要的方法实现:原生代码利用以前提到的代理方法,在用户点击头像后,拦截该URL,分析URL为头像部分,直接执行原生方法跳转到我的主页VC。
经过分析简书文章页面的网页源代码,我发现用户头像对应的URL
中的Query
部分,有一个参数为utm_medium=note-author-link
。据此,在- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
代理方法中加以判断,如果头像连接,则跳转到我的主页VC。下面是相关代码:浏览器
NSURL *destinationURL = request.URL;
NSString *URLQuery = destinationURL.query;
// 简书点击文章中头像时跳转至原生页面。此处利用头像连接中的一个参数做判断
if ([URLQuery containsString:@"utm_medium=note-author-link"])
{
NSLog(@"我跳转到我的主页啦");
AvatorViewController *avatorVC = [[AvatorViewController alloc] init];
[self.navigationController pushViewController:avatorVC animated:YES];
return NO;
}复制代码
最后返回NO是由于如果头像连接,该web页面是不须要作跳转操做的。bash
这里顺便讲一个小tips:若是想要在Mac端查看移动端的网页源代码,那么你只须要在Safari中输入该页面,而且在
开发
选项下的用户代理
中,选择iOS系统下的Safari做为代理,这时再使用源代码查看,看到的就是移动端的网页源代码了。服务器
这个特性的实现方式和上面相似,点击评论按钮,原生代码构建一条JS语句,交由- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
方法进行执行,由web页面执行滑动操做。代码以下:架构
- (void)scrollToCommentField
{
[self stringByEvaluatingJavaScriptFromString:@"scrollTo(0,20500)"];
}复制代码
这里的JS语句很是简单,因为笔者的前端知识还有所欠缺,没有想到能够精确滑动的评论区域的JS语句,因此简要实现,点到为止。app
这里首先须要贴一下文章页面的网页源代码:
<!-- 评论列表 -->
<div data-vcomp="comments-list" data-lazy="3">
<script type="application/json">
{"likedNote":true,"commentable":true,"publicCommentsCount":3,"noteId":2491941,"likesCount":43}
</script>
</div>复制代码
能够看到,页面的评论内容是异步加载的。因此这个功能的实现,我
认为比较合理的逻辑是原生组件向服务器提交一条新的评论,收到成功回调以后,原生组件和web页面进行交互,执行更新并加载评论列表的JS代码,从而看到本身发的新评论。
这里和点击头像的实现方法相似,经过拦截连接的URL,区份内部连接和第三方连接,从而在开始加载的时候采用不一样的加载界面,或者对于第三方连接单独开启一个第三方VC。
demo中关于第三方连接的关闭按钮的显示逻辑,作出了相应的处理。
看到这里你们应该就会发现,对于提到Hybrid咱们就会想到的Bridge、Router等模块,我并无作明显的限定。这样也是为了方便你们用一种更接近以往原生代码编写的思惟,来理解Hybrid模式。
同时,demo中较多涉及了原生代码对web页面作出的沟通操做。而没有JS代码对原生代码的调用,这是由于一来站在一个简书客户端的用户和iOS开发的角度,对于JS端执行的操做,有些力不能及,这本是和你共同工做的前端伙伴的任务,二来对于一篇帮助你们入门Hybrid的文章来讲,从这个单方面的交互来入手,管中窥豹,已经是足够。
其实,写了这么多,我以为收获到一些感悟是最重要的,下面的要讲的,多是我以为更为重要的思想性的东西。
进度条+返回、关闭按钮
的设计,则更符合用户在浏览器中进行阅读的习惯,也能够和本身的内容进行直观区分,这也改善了用户体验。对于文章内容,我写了一个demo,这是demo地址。为了便于理解,我为代码写了详尽的注释。若是以为它对你有帮助,不妨在github上为我点一个star~很是感谢!