学习WKWebView使用、JS的交互

前言:

目录:
一、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  navigationDelegate;
// UI代理
@property (nullable, nonatomic, weak) id  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..postMessage() 发送消息
 - (void)addScriptMessageHandler:(id )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 *)allWebsiteDataTypes;

 // 查找指定类型的缓存数据
 // 回调的值是WKWebsiteDataRecord的集合
 - (void)fetchDataRecordsOfTypes:(NSSet *)dataTypes completionHandler:(void (^)(NSArray *))completionHandler;

 // 删除指定的纪录
 // 这里的参数是通过上面的方法查找到的WKWebsiteDataRecord实例获取的
 - (void)removeDataOfTypes:(NSSet *)dataTypes forDataRecords:(NSArray *)dataRecords completionHandler:(void (^)(void))completionHandler;

 // 删除某时间后修改的某类型的数据
 - (void)removeDataOfTypes:(NSSet *)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 *dataTypes;

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

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

学习网站:

    1. WKWebView详解
  • 2.WKWebView的使用--API篇

你可能感兴趣的:(学习WKWebView使用、JS的交互)