Thinking in FE 更好用的 UIWebView

312 阅读5分钟
原文链接: mp.weixin.qq.com

作者:翁阳 (沪江iOS架构师)
本文原创,转载请注明作者及出处。

继上一篇 Thinking 以来过去太久时间了,这些日子里一直在疲于奔命,无论如何繁忙,静下来写写博客还是一件非常值得的事情。

本篇我们来谈谈UIWebView,虽然在 iOS 8.0 之后更加推荐使用WKWebView,但在你没放弃 iOS 7.0 之前,不妨看看如何让这陈旧的UIWebView更加好用些,当然这里的一些思想同样可以迁移到 WKWebView中。

用 WebView 的动机

在深入这个话题之前,我们先静下心来想想,一般在什么情况下我们会想到使用WebView?作为大前端名副其实的粘合剂, WebView无疑是移动端和触屏端最直接的连接方式。那么这个答案就很简单了,我们要接入触屏页面(HTML5),那为什么我们要接入触屏页面呢?或许是因为下面这些点:

  • 跨平台,减少开发资源

  • 热更新,避免发版等待

  • 多团队研发,集成成本低

  • 作为平台扩展,开发者门槛低

作为一个能够将触屏页很好的嵌入的WebView而言,并非简单的加载一个 URL就足够的,我们一直都在致力于将触屏的体验和原生拉进,所以,原生和触屏之间的交互必不可少。

浅谈 JS && Native 交互

如何在UIWebView实现 JavascriptNative的相互调用呢?目前大致有两种方案:JSBridgeJSCore

JSBridge

JSBridge实现的原理其实很简单,核心依赖于 UIWebViewUIWebViewDelegate的两个方法:

  1. stringByEvaluatingJavaScriptFromString:

  2. webView:shouldStartLoadWithRequest:navigationType:

第一个方法用于Native调用 Javascript方法,实现了原生调用触屏这一条道路。那么触屏如果想调用原生,可以使用 iframe构建一个 load请求,原生端通过第二个回调方法进行拦截,根据 URL中所传递的信息重定向到特定的原生方法,虽然有点绕,但这条道路依然是行通了。

对于此方法,已经有相关比较好的开源库,比如 WebViewJavascriptBridge。

JSCore

JSCore也就是 WebKit中的 JavascriptCore,这是一个非常直接的互通方式。最核心的类是 JSContext,通过这个上下文,我们可以注入一个 OC 对象到 JS 的运行环境中,我们也可以通过这个上下文直接调用 JS 方法。具体就不展开,可以参考JavascriptCore的 API 文档。

如何组织好代码

在给UIWebView增加了很多业务相关功能后,常常会发现这个 Controller会变得很庞大,又或是出现了很多类似的 Controller,这通常会给后续的维护带来困扰。那么如何更好的来组织这些代码呢?

在面向对象的程序设计里,针对结构优化上通常有两个方向的思维:提取间接层拆解再组合,当然这是我总结出来的思维方式。提取间接层是一种纵向的维度,通常包括:

  • 提取公共基类

  • 增加 Mediator,隔离依赖

而“拆解再组合”是一种横向的维度,将一个大的代码结构进行模块划分,最后再进行组合。

面对不同的结构问题,不同的人优化的方式也会有所不同,我更加倾向于“拆解再组合”。过多的层次很容易导致调用路劲过长,我觉得这并不易于理解,另外面向对象中常说组合胜于继承,这其中的缘由也就不展开了。

一个轻量、无侵入的方案

KakiWebView,一个简单易读的封装,核心思想便是采用了拆解再组合,以下是我设计的初衷:

  • 对于现有的UIWebView无侵入性的使用

  • 可扩展性强,可实现自定义扩展

  • 简单易用,学习成本低

项目地址:https://github.com/prinsun/KakiWebView

无论你现在的UIWebView是以何种方式使用的,都可以轻而易举的使用 KakiWebView来获得扩展能力,该项目中已经内置了一些 Plugin

  • KakiJavascriptCorePlugin:可注入 OC 对象到Javascript环境中,使用上 JSCore的交互方式

  • KakiProgressPlugin:基于NJKWebViewProgress的一个进度条插件,安装该插件后 UIWebView加载页面时将会带有进度显示

  • KakiPopGesturePlugin:屏幕边缘侧滑返回的插件,模仿 Safari 的回退效果

  • KakiTitleObserverPlugin:监控网页 Title 变化的插件,可以实时获取到网页标题变化

这些内置的插件非常通用,也是大多项目中所需要的,使用的方式非常简单:

// 启用 Kaki
[self.webView setEnableKakiPlugins:YES];

// 安装 Kaki 插件
[self.webView installKakiPlugin:[KakiProgressPlugin.alloc init]];
[self.webView installKakiPlugin:[KakiPopGesturePlugin.alloc init]];
[self.webView installKakiPlugin:[KakiTitleObserverPlugin.alloc init]];

// 配置插件
__weak __typeof(self) wself = self;
[self.webView.titleObserverPlugin setOnTitleChanged:^(NSString *title) {
   wself.titleLabel.text = title;
}];
self.webView.progressPlugin.progressColor = [UIColor redColor];


内置的这些插件是你学习如何自定义插件的很好示例,详细请阅读源码。

构建适合你的容器

当然 KakiWebView 绝对不能满足你所有的业务需求,而这个项目的初衷也并不是要完成一个大而全的万能WebView,正确的定位是一个提供了扩展UIWebView能力的基础组件。通过这样一个基础组件,自定义符合你业务需求的 Plugin,从而构建一个真正符合你所期望的容器。

如果你并不喜欢JSCore的交互方式,你完全可以自定义一个 Plugin来实现 JSBridge的交互方式。另外可以通过自定义一个 Plugin配合 NSURLProtocol,实现离线浏览这样的功能。总而言之,有了这样的一个基础组件,无论是从代码的组织还是后续的扩展,都有很大的帮助。

那么,放飞思想,重构又或是去构建一个更强大的 Web 容器吧!

相关文章:



本文对你有帮助?欢迎扫码加入前端学习小组微信群: