装饰模式整合UIWebView与WKWebView

浏览: 151 发布日期: 2016-10-21 分类: objective-c

写在前面:
1、在阅读过无数关于WebView的文章后,才有此文的出现。某种意义上,此文的初衷并非技术分享,而是对抄袭的不满。希望阅读此文的你是干干净净的。
2、选择WebView作为第一篇技术文章的原因,是因为对于第二代webview引擎的介绍不尽人意,且关于WKWebView的JS交互极为模糊,以至于很长一段时间内,我司工程师,也因此方面知识匮乏,无力替换更新webview。而到这一刻止,两代webview的整合与交互都可在源码中一窥究竟。
3、源码地址:GSWebView
4、如果你有更加高明的思路,请Email:xorshine@icloud.com,或者在github上说明。本文并不讲解基础用法。


本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 中国大陆许可协议进行许可。


1.为何要从UIWebView更新到WKWebView?

性能测试:
UIWebView WKWebView 备注
iOS 版本 8.4 8.4 ———
iPhone 6 6 真机测试
测试网页 天猫首页 天猫首页 ———
内存占用峰值 132.2MB 8.4MB ———
加载耗时 3.1s 2.6s mach_absolute_time();
FPS 无特别明显差异 无明显差异 Instruments (core animation)
测试次数 2 2 ———

内存占用这一点考虑,使用WKWebView便是明智的选择。


2.如UIWebView一样使用WKWebView,用熟悉的API开发

还要适配iOS7,停止无休止的系统判断,那么,看看下面,让你解脱

使用介绍:同样的款式如何打造不一样的内涵?

熟悉的属性、方法

@property (nonatomic, readonly, strong) UIScrollView *scrollView;
@property (nonatomic, readonly) BOOL canGoBack;
@property (nonatomic, readonly) BOOL canGoForward; 

- (void)reload;
- (void)stopLoading;
- (void)goBack;
- (void)goForward;

形神皆似的协议方法

#prama mark - GSWebViewDelegate
- (BOOL)gswebView:(GSWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(GSWebViewNavigationType)navigationType;
- (void)gswebViewDidStartLoad:(GSWebView *)webView;
- (void)gswebViewDidFinishLoad:(GSWebView *)webView;
- (void)gswebView:(GSWebView *)webView didFailLoadWithError:(NSError *)error;  

JS交互重点
GSWebView定义了两套协议GSWebViewDelegate和GSWebViewJavaScript,GSWebViewDelegate定义了加载状态,GSWebViewJavaScript则只定义了JS交互。

#prama mark - GSWebViewJavaScript
 /**
   交互协议
 */
@protocol GSWebViewJavaScript <NSObject>
@optional

/**
   调用OC方法
     
     - (NSArray<NSString *>*)gswebViewRegisterObjCMethodNameForJavaScriptInteraction
     {
        return @[@"getCurrentUserId"];
     }
     //当JS调用一个'- (void)getCurrentUserName:(NSString *)name'的OC方法时,参数name由JS传来,
     //那么在实现该OC方法时,只需要正确知道参数类型或基本结构,你也可以写为id类型做普适,在方法内部做转换。
     - (void)getCurrentUserId:(NSString *)Id
     {
        NSLong@(@"JS调用到OC%@",Id);
     }
 */
- (NSArray<NSString *>*)gswebViewRegisterObjCMethodNameForJavaScriptInteraction;

@end

3.JavaScript源码必须做出的改动!

  • WKWebView的JS交互,最不惹人注目但最为关键的地方在于此。

  • 在UIWebView的时代,想要JS交互,JS代码不需要做出改动,但是在WKWebView时代,JS需要根据客户端版本号调用不同的方法与与客户端进行交互。

官方文档:

Adding a scriptMessageHandler adds a function window.webkit.messageHandlers.<name>.postMessage(<messageBody>) for all frames.

举例说明:
JS中有一个getConsultationInfo(id)方法,客户端获取到id实现该方法,这是UIWebView时代

但是在GSWebView中,必须这样:

//获取客户端iOS版本
var version = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);  
version = parseInt(ver[1], 10);  

if(version >= 7.0 && version < 8.0){
    getConsultationInfo(id);
}else if(version>=8.0){
    window.webkit.messageHandlers.getConsultationInfo.postMessage(id)
} 

4.GSWebView采用装饰模式的实现整合的思路

  • GSWebView内部一个UIView指针,当调用指定构造方法初始化后,内部根据不同的系统版本,将UIView指针指向WKWebView或者UIWebView。

  • GSWebView源码显式并未遵守任何协议,但初始化时,GSWebView应该遵循的协议都通过Runtime动态绑定。

- (void)registerProtocol:(Protocol *)protocol
{
    if (protocol){
        objc_registerProtocol(protocol);
        class_addProtocol([GSWebView class], protocol)?:NSLog(@"动态绑定协议失败");
    }
}

这样的好处在于,当内部由UIWebView实现时,其实并不需要遵守WKwebView的协议,只取所需。

  • 关于回调,除去UI方面的进度回调通过GSWebViewDelegate协议,在GSWebView中注册需要的JS调用的OC方法,都通过一个指向函数的指针实现回调,且回调线程为主线程。

  • OC调用JS的回调则在一个block中完成,且回调线程为主线程。



本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 中国大陆许可协议进行许可。


返回顶部