学习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 <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
           }];
       }
   }
     }];

学习网站:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 197,511评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,011评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 144,489评论 0 326
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,872评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,741评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,525评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,914评论 3 388
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,542评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,833评论 1 293
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,858评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,674评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,479评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,923评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,099评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,400评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,923评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,141评论 2 339

推荐阅读更多精彩内容