美文网首页程序员iOS Developer
WKWebView的使用、优化和封装(VDWebView)

WKWebView的使用、优化和封装(VDWebView)

作者: volientDuan | 来源:发表于2018-12-22 14:04 被阅读63次

    VDWebView的源码和使用示例

    VDWebView带来的便捷

    • 以最少的成本替代旧项目中的UIWebView
      • 熟悉的代理方法
      • 常用的基本方法和属性
      • 使用起来更加的熟悉只需把UIWebView名更换为VDWebView
    • 更加方便和安全的JS调用OC方法(后面我会具体说明解决方案)
      • 支持以target-action的方式替代delegate(两者任意选择)
      • 不会出现类似于使用WKWebView注册OC方法忘记注销导致循环引用无法释放的问题
    • 提供加载进度条的使用、预估进度值的读取等

    CocoaPods

    pod 'VDWebView', '~> 1.0.0'
    

    基本描述

    • 为什么用的是WKWebView
    • 提供熟悉和更加便捷的属性和方法(结合了UIWebViewWKWebView)
    • JS调用OC方法的绑定
    • JS的调用和注入

    为什么使用WKWebView

    • WKWebView是iOS8后推出的WebKit框架中的控件,由于iOS12后已经弃用UIWebView了而且现在的大多数项目只适配到iOS8
    • 加载速度优于UIWebView且解决了加载网页时的内存泄露问题
    • 在和JS交互方面提供了桥梁WKUserContentController
    • 没好处这东西出来干嘛,所以综上用起来吧

    提供了哪些更加熟悉和更加便捷的属性和方法

    VDWebViewDelegate

    • UIWebView代理方法,处理对象(WKNavigationDelegate)
    /// 类UIWebView代理方法
    - (void)webViewDidStartLoad:(VDWebView *)webView;
    - (void)webViewDidFinishLoad:(VDWebView *)webView;
    - (void)webView:(VDWebView *)webView didFailLoadWithError:(NSError *)error;
    - (BOOL)webView:(VDWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
    
    • WKUIDelegateWKScriptMessageHandler代理的合并
    /**
     JS调原生代理方法(注册了过的方法将全部通过此方法回调)
    
     @param webView VDWebView
     @param message 回调消息
     */
    - (void)webView:(VDWebView *)webView didReceiveScriptMessage:(WKScriptMessage *)message;
    
    /**
     JS弹框拦截方法--如果需要自定义弹框建议声明此方法
    
     @param webView VDWebView
     @param type 弹框类型
     @param title 标题
     @param content 内容
     @param completionHandler 结果处理必须执行completionHandler(data)
     */
    - (void)webView:(VDWebView *)webView showAlertWithType:(VDJSAlertType)type title:(NSString *)title content:(NSString *)content completionHandler:(void (^)(id))completionHandler;
    

    新增了哪些属性和方法

    ///预估网页加载进度
    @property (nonatomic, readonly) CGFloat estimatedProgress;
    // 是否显示进度条 默认不显示
    @property (nonatomic, assign) BOOL isShowProgressBar;
    ///进度条
    @property (nonatomic, strong) UIView *progressBar;
    /**
     web页面加载完毕后的内容高度(在页面加载完成后获取)
     */
    @property (nonatomic, readonly) CGFloat *contentHeight;
    /// 是否启用js调用原生弹框 默认为NO 禁止(默认加载弹框在根试图)
    @property (nonatomic, assign) BOOL enableAllAlert;
    @property (nonatomic, assign) BOOL enableAlert;
    @property (nonatomic, assign) BOOL enableConfirm;
    @property (nonatomic, assign) BOOL enablePrompt;
    
    ///back 层数
    - (NSInteger)countOfHistory;
    - (void)gobackWithStep:(NSInteger)step;
    
    

    JS调用OC方法的绑定

    在使用WKWebView时我们需要调用WKWebViewconfiguration中的userContentController所属类WKUserContentController提供的实例方法进行注册,具体方法如下:

    - (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
    

    对应的注销方法为:

    - (void)removeScriptMessageHandlerForName:(NSString *)name;
    

    已知的循环引用问题

    在使用addScriptMessageHandler:name:方法注册时传入的这个handler被循环引用,如果不调用对应的注销方法就会导致handler这个对象无法被释放,如果你这个handler传入是webView所在的控制器,那么你就要在销毁这个控制器前注销掉你注册的方法.

    tip: 如何知道控制器有没有被释放,重写dealloc(),没走此方法说明未被释放

    VDWebView是如何解决循环引用问题

    简要分析可分为下面三步

    • 使用VDScripMessageHandler作为注册的handler
      • 继承协议WKScriptMessageHandler
      • 提供target-action回调方式
    • 保存注册记录
    • VDWebViewdealloc()方法中获取注册记录并注销

    这些做的好处在于你在使用VDWebView时无需自己去一个个手动注销了(如果你注册的方法多的话那就是噩梦了)

    VDWebView是如何进行方法的注册和回调的

    • 方法的注册
    - (void)addScriptMessageHandler:(id)scriptMessageHandler name:(NSString *)name;
    
    • JS调用说明
    // 没效果可使用try-catch
    window.webkit.messageHandlers.#OC方法名#.postMessage(#参数#)
    
    

    回调方式分两种:delegate和target-action; 两种方式只能存一,优先delegate

    • delegate方式,只需在控制器中声明VDWebViewDelegate中的方法
    - (void)webView:(VDWebView *)webView didReceiveScriptMessage:(WKScriptMessage *)message;
    
    • target-action方式
      • 不能声明上述的代理方法
      • 在控制器(方法注册传入的scriptMessageHandler)中声明同名的OC方法

    为什么要增加target-action的方式

    target-action:目标-动作模式,拜C语言所赐,更是灵活很多,编译期没有任何检查,都是运行时的绑定

    • VDWebView中就是通过NSSelectorFromString()动态加载方法,再通过NSMethodSignatureNSInvocation进行方法的签名和调用
    • 这样就可以充分的体现JS调用对应的OC方法一对一更加清晰并且方便处理

    JS的调用和注入

    可通过两种方式进行JS方法的调用,推荐第一种

    • WKWebView的同名方法
    - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler;
    
    
    • UIWebView的同名方法(不建议使用这个办法,因为会在内部等待执行结果)
    - (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)javaScriptString;
    

    脚本的注入和移除

    /**
     注入脚本(js...)
    
     @param source 注入的内容
     @param injectionTime 注入时间
     @param mainFrameOnly 只作用主框架
     */
    - (void)addUserScriptWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)mainFrameOnly;
    
    /**
     移除所有注入的脚本
     */
    - (void)removeAllUserScripts;
    

    后续版本思考和设计中

    • cookie的处理
    • APP和web资源共享问题:比如图片
    • 拦截webView内部请求通过自定义URL的方式进行JS交互

    尾声

    愿意了解或者有任何疑问的欢迎指教,有何不足多多担待!

    相关文章

      网友评论

        本文标题:WKWebView的使用、优化和封装(VDWebView)

        本文链接:https://www.haomeiwen.com/subject/xzyikqtx.html