美文网首页
学习WKWebView使用、JS的交互

学习WKWebView使用、JS的交互

作者: 麦穗0615 | 来源:发表于2018-08-19 17:21 被阅读112次

    前言:

    目录:
    一、WKWebView基本介绍
    二、WKWebView新特性
    三、WebKit框架概览
    四、WKWebView的属性

    一、WKWebView基本介绍

    iOS8之前,应用浏览网页通常使用UIWebView这样一个类,由于这个类加载速度慢、占用内存大、还有内存泄漏的问题存在,通常需要开发者自己优化。

    所以,在Apple WWDC 2014随iOS8和OS X 10.10之后,苹果推出了新的网页框架WebKit,优化了UIWebView的内存问题,功能非常的强大,WebKit中更新的WKWebView控件的新特性与使用方法,它很好的解决了UIWebView存在的内存、加载速度等诸多问题。

    二、WKWebView新特性

    • 在性能、稳定性、功能方面有很大提升(最直观的体现就是加载网页是占用的内存);

    • 允许JavaScript的Nitro库加载并使用(UIWebView中限制);

    • 支持了更多的HTML5特性;

    • 高达60fps的滚动刷新率以及内置手势;

    • 将UIWebViewDelegate与UIWebView重构成了14类与3个协议查看苹果官方文档

    三、WebKit框架概览

    WebKit框架概览

    如上图所示,WebKit框架中最核心的类应该属于WKWebView了,这个类专门用来渲染网页视图,其他类和协议都将基于它和服务于它。

    • WKWebView:网页的渲染与展示,通过WKWebViewConfiguration可以进行自定义配置。

    • WKWebViewConfiguration:这个类专门用来配置WKWebView。

    • WKPreference:这个类用来进行相关webView设置。

    • WKProcessPool:这个类用来配置进程池,与网页视图的资源共享有关。

    • WKUserContentController:这个类主要用来做native与JavaScript的交互管理。

    • WKUserScript:用于进行JavaScript注入。

    • WKScriptMessageHandler:这个类专门用来处理JavaScript调用native的方法。

    • WKNavigationDelegate:这网页跳转间的导航管理协议,这个协议可以监听网页的活动。

    • WKNavigationAction:网页某个活动的示例化对象。

    • WKUIDelegate:用于交互处理JavaScript中的一些弹出框。

    • WKBackForwardList:堆栈管理的网页列表。

    • WKBackForwardListItem:每个网页节点对象。

    四、WKWebView的属性

    4.1 常用属性

    // 导航代理
    @property (nullable, nonatomic, weak) id <WKNavigationDelegate> navigationDelegate;
    // UI代理
    @property (nullable, nonatomic, weak) id <WKUIDelegate> UIDelegate;
    // 页面标题, 一般使用KVO动态获取
    @property (nullable, nonatomic, readonly, copy) NSString *title;
    // 页面加载进度, 一般使用KVO动态获取
    @property (nonatomic, readonly) double estimatedProgress;
    
    // 可返回的页面列表, 已打开过的网页, 有点类似于navigationController的viewControllers属性
    @property (nonatomic, readonly, strong) WKBackForwardList *backForwardList;
    
    // 页面url
    @property (nullable, nonatomic, readonly, copy) NSURL *URL;
    // 页面是否在加载中
    @property (nonatomic, readonly, getter=isLoading) BOOL loading;
    // 是否可返回
    @property (nonatomic, readonly) BOOL canGoBack;
     // 是否可向前
    @property (nonatomic, readonly) BOOL canGoForward;
    // WKWebView继承自UIView, 所以如果想设置scrollView的一些属性, 需要对此属性进行配置
    @property (nonatomic, readonly, strong) UIScrollView *scrollView;
    // 是否允许手势左滑返回上一级, 类似导航控制的左滑返回
    @property (nonatomic) BOOL allowsBackForwardNavigationGestures;
    
     /// 在iOS上默认为NO,标识不允许链接预览
    @property (nonatomic) BOOL allowsLinkPreview API_AVAILABLE(macosx(10.11), ios(9.0));
    /// 网页链接是否安全
    @property (nonatomic, readonly) BOOL hasOnlySecureContent;
    /// 证书服务
    @property (nonatomic, readonly, nullable) SecTrustRef serverTrust API_AVAILABLE(macosx(10.12), ios(10.0));
    //自定义UserAgent, 会覆盖默认的值 ,iOS 9之后有效
    @property (nullable, nonatomic, copy) NSString *customUserAgent;
    
    /// 是否支持放大手势,默认为NO
    @property (nonatomic) BOOL allowsMagnification;
    /// 放大因子,默认为1
    @property (nonatomic) CGFloat magnification;
     /// 据设置的缩放因子来缩放页面,并居中显示结果在指定的点
    - (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point;
    /// 证书列表
    @property (nonatomic, readonly, copy) NSArray *certificateChain API_DEPRECATED_WITH_REPLACEMENT("serverTrust", macosx(10.11, 10.12), ios(9.0, 10.0));
    

    4.2 一些方法

    // 带配置信息的初始化方法
    // configuration 配置信息
    - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
    // url加载webView视图
    - (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
     /// 文件加载webView视图
     - (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL API_AVAILABLE(macosx(10.11), ios(9.0));
    
    // HTMLString字符串加载webView视图
    - (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
     /// NSData数据加载webView视图
     - (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL API_AVAILABLE(macosx(10.11), ios(9.0));
     /// 返回上一个网页节点
     - (nullable WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item;
    // 返回上一级
    - (nullable WKNavigation *)goBack;
    // 前进下一级, 需要曾经打开过, 才能前进
    - (nullable WKNavigation *)goForward;
    // 刷新页面
    - (nullable WKNavigation *)reload;
     // 根据缓存有效期来刷新页面
    - (nullable WKNavigation *)reloadFromOrigin;
    // 停止加载页面
    - (void)stopLoading;
    // 执行JavaScript代码
    - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
    

    4.3 WKWebViewConfiguration

     // 通过此属性来执行JavaScript代码来修改页面的行为
     @property (nonatomic, strong) WKUserContentController *userContentController;
    
     //***********下面属性一般不需要设置
     // 首选项设置,  
     //可设置最小字号, 是否允许执行js
     //是否通过js自动打开新的窗口
     @property (nonatomic, strong) WKPreferences *preferences;
     // 是否允许播放媒体文件
     @property (nonatomic) BOOL allowsAirPlayForMediaPlayback
     // 需要用户来操作才能播放的多媒体类型
     @property (nonatomic) WKAudiovisualMediaTypes mediaTypesRequiringUserActionForPlayback
     // 是使用h5的视频播放器在线播放, 还是使用原生播放器全屏播放
     @property (nonatomic) BOOL allowsInlineMediaPlayback;
    

    4.4 WKUserContentController
    WKUserContentController 是JavaScript与原生进行交互的桥梁, 主要使用的方法有:

     // 注入JavaScript与原生交互协议
     // JS 端可通过 window.webkit.messageHandlers.<name>.postMessage(<messageBody>) 发送消息
     - (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
     // 移除注入的协议, 在deinit方法中调用
     - (void)removeScriptMessageHandlerForName:(NSString *)name;
    
     // 通过WKUserScript注入需要执行的JavaScript代码
     - (void)addUserScript:(WKUserScript *)userScript;
     // 移除所有注入的JavaScript代码
     - (void)removeAllUserScripts;
    

    使用WKUserContentController注入的交互协议, 需要遵循WKScriptMessageHandler协议, 在其协议方法中获取JavaScript端传递的事件和参数:

    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
    

    WKScriptMessage包含了传递的协议名称及参数, 主要从下面的属性中获取:

    // 协议名称, 即上面的add方法传递的name
    @property (nonatomic, readonly, copy) NSString *name;
    // 传递的参数
    @property (nonatomic, readonly, copy) id body;
    

    4.5 WKUserScript
    WKUserScript用于往加载的页面中添加额外需要执行的JavaScript代码, 主要是一个初始化方法:

    /*
     source: 需要执行的JavaScript代码
     injectionTime: 加入的位置, 是一个枚举
     typedef NS_ENUM(NSInteger, WKUserScriptInjectionTime) {
         WKUserScriptInjectionTimeAtDocumentStart,
         WKUserScriptInjectionTimeAtDocumentEnd
     } API_AVAILABLE(macosx(10.10), ios(8.0));
    
     forMainFrameOnly: 是加入所有框架, 还是只加入主框架
     */
     - (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly;
    

    4.6 WKUIDelegate
    这个代理方法, 主要是用来处理使用系统的弹框来替换JS中的一些弹框的,比如: 警告框, 选择框, 输入框, 主要使用的是下面三个代理方法:

     /**
      webView中弹出警告框时调用, 只能有一个按钮
    
    @param webView webView
    @param message 提示信息
    @param frame 可用于区分哪个窗口调用的
    @param completionHandler 警告框消失的时候调用, 回调给JS
      */
    - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"警告" message:message preferredStyle:(UIAlertControllerStyleAlert)];
    UIAlertAction *ok = [UIAlertAction actionWithTitle:@"我知道了" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
       completionHandler();
     }];
    
     [alert addAction:ok];
     [self presentViewController:alert animated:YES completion:nil];
     }
    

    .

     /** 对应js的confirm方法
      webView中弹出选择框时调用, 两个按钮
    
      @param webView webView description
      @param message 提示信息
      @param frame 可用于区分哪个窗口调用的
      @param completionHandler 确认框消失的时候调用, 回调给JS, 参数为选择结果: YES or NO
    */
     - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {
    
     UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"请选择" message:message preferredStyle:(UIAlertControllerStyleAlert)];
     UIAlertAction *ok = [UIAlertAction actionWithTitle:@"同意" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
       completionHandler(YES);
     }];
    
     UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"不同意" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
       completionHandler(NO);
     }];
    
     [alert addAction:ok];
     [alert addAction:cancel];
     [self presentViewController:alert animated:YES completion:nil];
     }
    

    .

    /** 对应js的prompt方法
    webView中弹出输入框时调用, 两个按钮 和 一个输入框
    
    @param webView webView description
    @param prompt 提示信息
    @param defaultText 默认提示文本
    @param frame 可用于区分哪个窗口调用的
    @param completionHandler 输入框消失的时候调用, 回调给JS, 参数为输入的内容
    */
    - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler {
    
    
     UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"请输入" message:prompt preferredStyle:(UIAlertControllerStyleAlert)];
    
    
     [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
       textField.placeholder = @"请输入";
     }];
    
     UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
       
       UITextField *tf = [alert.textFields firstObject];
       
               completionHandler(tf.text);
     }];
    
     UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
                completionHandler(defaultText);
     }];
    
     [alert addAction:ok];
     [alert addAction:cancel];
     [self presentViewController:alert animated:YES completion:nil];
     }
    

    4.7 WKNavigationDelegate

    // 决定导航的动作,通常用于处理跨域的链接能否导航。
    // WebKit对跨域进行了安全检查限制,不允许跨域,因此我们要对不能跨域的链接单独处理。
    // 但是,对于Safari是允许跨域的,不用这么处理。
    // 这个是决定是否Request
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    //  在发送请求之前,决定是否跳转
    decisionHandler(WKNavigationActionPolicyAllow);  
    }
    

    .

    // 是否接收响应
     - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
         // 在收到响应后,决定是否跳转和发送请求之前那个允许配套使用
       decisionHandler(WKNavigationResponsePolicyAllow);
     }
    

    .

     //用于授权验证的API,与AFN、UIWebView的授权验证API是一样的
     - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler{
    
       completionHandler(NSURLSessionAuthChallengePerformDefaultHandling ,nil);
     }
    

    .

     // main frame的导航开始请求时调用
     - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
    
     }
    

    .

     // 当main frame接收到服务重定向时调用
     - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
         // 接收到服务器跳转请求之后调用
     }
    

    .

     // 当main frame开始加载数据失败时,会回调
     - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
    
     }
    

    .

     // 当内容开始返回时调用
     - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation{  
     }
    

    .

     //当main frame导航完成时,会回调
      - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
          // 页面加载完成之后调用
      }
    

    .

     // 当main frame最后下载数据失败时,会回调
     - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error >      {
     }
    

    .

     // 当web content处理完成时,会回调
     - (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
     }
    

    4.8 WKWebsiteDataStore
    WKWebsiteDataStore 提供了网站所能使用的数据类型,包括 cookies,硬盘缓存,内存缓存活在一些WebSQL的数据持久化和本地持久化。可通过 ** WKWebViewConfiguration类的属性 websiteDataStore 进行相关的设置。WKWebsiteDataStore** 相关的API也比较简单:

     // 默认的data store
     + (WKWebsiteDataStore *)defaultDataStore;
    
     // 如果为webView设置了这个data Store,则不会有数据缓存被写入文件
     // 当需要实现隐私浏览的时候,可使用这个
     + (WKWebsiteDataStore *)nonPersistentDataStore;
    
     // 是否是可缓存数据的,只读
     @property (nonatomic, readonly, getter=isPersistent) BOOL persistent;
    
     // 获取所有可使用的数据类型
     + (NSSet<NSString *> *)allWebsiteDataTypes;
    
     // 查找指定类型的缓存数据
     // 回调的值是WKWebsiteDataRecord的集合
     - (void)fetchDataRecordsOfTypes:(NSSet<NSString *> *)dataTypes completionHandler:(void (^)(NSArray<WKWebsiteDataRecord *> *))completionHandler;
    
     // 删除指定的纪录
     // 这里的参数是通过上面的方法查找到的WKWebsiteDataRecord实例获取的
     - (void)removeDataOfTypes:(NSSet<NSString *> *)dataTypes forDataRecords:(NSArray<WKWebsiteDataRecord *> *)dataRecords completionHandler:(void (^)(void))completionHandler;
    
     // 删除某时间后修改的某类型的数据
     - (void)removeDataOfTypes:(NSSet<NSString *> *)websiteDataTypes modifiedSince:(NSDate *)date completionHandler:(void (^)(void))completionHandler;
    
     // 保存的HTTP cookies
     @property (nonatomic, readonly) WKHTTPCookieStore *httpCookieStore
    

    dataTyle

     // 硬盘缓存
     WKWebsiteDataTypeDiskCache,
    
     // HTML离线web应用程序缓存
     WKWebsiteDataTypeOfflineWebApplicationCache,
    
     // 内存缓存
     WKWebsiteDataTypeMemoryCache,
    
     // 本地缓存
     WKWebsiteDataTypeLocalStorage,
    
     // cookies
     WKWebsiteDataTypeCookies,
    
     // HTML会话存储
     WKWebsiteDataTypeSessionStorage,
    
     //  IndexedDB 数据库
     WKWebsiteDataTypeIndexedDBDatabases,
    
     // WebSQL 数据库
     WKWebsiteDataTypeWebSQLDatabases
    

    WKWebsiteDataRecord

     // 展示名称, 通常是域名
     @property (nonatomic, readonly, copy) NSString *displayName;
    
     // 包含的数据类型
     @property (nonatomic, readonly, copy) NSSet<NSString *> *dataTypes;
    

    简单应用
    删除指定时间的所有类型数据

     WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
         [dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] >completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) {
       for (WKWebsiteDataRecord *record in records) {
           [dataStore removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{
               // done
           }];
       }
         }];
    

    查找删除特定的内容

     WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
         [dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] >completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) {
       for (WKWebsiteDataRecord *record in records) {
           if ([record.displayName isEqualToString:@"baidu"]) {
               [dataStore removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{
                   // done
               }];
           }
       }
         }];
    

    学习网站:

    相关文章

      网友评论

          本文标题:学习WKWebView使用、JS的交互

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