WKWebView

在iOS中,加载网页目前有两种控件:UIWebView和WKWebView。

UIWebView自iOS2就有,WKWebView从iOS8.0(2014年9月WWDC)才有,毫无疑问,WKWebView相对UIWebVIew要优秀得多,主要表现在以下几点:

1,WKWebView拥有60fps的滚动刷新率,页面更流畅

2,WKWebView拥有与Safari中相同的Nitro JavaScript引擎,大大提高了页面JS执行速度;但是UIWebView不支持Nitro JavaScript引擎,所以加载较慢。

3,WKWebView的内存占用大概是UIWebView的1/3~1/4,内存占用更低(可以查看该文:https://www.jianshu.com/p/181889939a85)。

4,WKWebView增加了estimatedProgress属性用以实现进度条。

5,WKWebView可以和JS直接互调函数,交互更方便;而UIWebView则需要依靠WebViewJavaScriptBridge第三方库来协助处理与JS的交互,实现起来较繁琐。

6,WKWebView是多进程组件,这意味着会从APP内存中分离内存到单独的进程中。但WKWebView的内存超过系统分配给它的内存的时候,WKWebView浏览器就会崩溃白屏,但是APP不会crash(APP会收到系统通知,并且尝试去重新加载页面)。

相反,UIWebView是和APP是同一个进程,UIWebView加载页面占用的内存被计算为APP内存占用的一部分,当APP超过了系统分配的内存,则会被操作系统crash。

7,UIWebView对html5的各种规范支持较少,而WKWebView由于是基于WebKit所以对h5的各种规则基本都支持。

以上介绍了WKWebView的优点,但是其也有以下缺点:

1,WKWebView需要iOS9及更高的版本,虽然WKWebView是在iOS8之后引入的,但是iOS8的版本存在重大限制,比如无法访问本地存储的文件,而支持比较全面是在iOS9以后的版本。

2,截屏捕获在WKWebView上会随机失败,因此,如果截屏的API是App中的关键操作,那么建议使用现有的UIWebView浏览引擎。

其他的区别如下:

1,UIWebView是UIKit框架的一部分,可以在应用程序内使用,无需导入任何内容;而WKWebView使用的是WebKit.framework,使用的时候需要导入到应用程序中。

WKWebView的用法

WKWebView API

WKWebView对象可以显示交互式Web内容,例如应用内浏览器。你可以使用WKWebView类将web内容嵌入到你的应用程序中。

使用概览

使用 - initWithFrame:configuration: 创建一个新的WKWebView对象。

然后就可以通过 - loadHTMLString:baseURL: 方法或者 - loadRequest: 方法加载网页内容。

可以使用stopLoading方法来停止页面的加载,使用loading属性来查看是否正在加载。

要允许用户在Web历史页面中前进或者后退,要为按钮设置goBack或者goForward的动作。当用户不能在某个方向上再移动时,使用canGoBack或者canGoForward来禁用按钮。

默认情况下,Web视图会自动将出现在Web内容中的电话号码转换成电话链接。当电话链接被点击时,电话应用程序就会启动并拨打该号码。要关闭这个默认的行为,用 WKDataDetectorTypes 设置 dataDetectorTypes 属性以不包含 WKDataDetectorTypePhoneNumber 标志。

你还可以使用 setMagnification:centeredAtPoint: 以编程方式设置Web内容第一次在Web视图中显示的缩放比例。 此后,用户可以使用手势来改变比例。

初始化Web视图

  • configuration。用于初始化web视图的配置副本。
  • - initWithFrame:configuration: 。用指定的frame和configuration初始化视图。

查看web信息

  • scrollView。与WebView相关联的滚动视图。
  • title。页面标题
  • URL。活动网址
  • customUserAgent。自定义用户代理字符串

设置委托

  • navigationDelegate
  • UIDelegate

加载内容

  • estimatedProgress。值为0-1,表示当前页面加载的进度。
  • hasOnlySecureContent。布尔值,表示页面上的所有资源是否通过安全加密的连接加载。
  • - loadHTMLString:baseURL:。设置网页内容和baseUrl
  • loading。布尔值,显示当前页面是否正在加载。
  • - reload。重新加载当前页面。
  • - reloadFromOrigin。重新加载当前页面,如果可能,使用缓存验证条件执行端到端重新验证。
  • - stopLoading。停止加载当前页面所有资源。
  • -loadData: MIMEType: characterEncodingName: baseURL:
  • -loadFileURL: allowingReadAccessToURL:

缩放内容

  • allowsMagnification。布尔值,表示放大手势是否会改变网页视图的放大倍数。
  • magnification。页面内容当前的缩放因子,默认是1
  • - setMagnification:centeredAtPoint:。按指定的因子缩放页面内容,并将结果居中在指定的点上。

导航

  • allowsBackForwardNavigationGestures。布尔值,指示水平滑动手势是否会触发后退列表导航,默认为NO。
  • backForwardList。网页视图的后退列表,即之前访问过的web页面的列表。
  • canGoBack。布尔值,指示后退列表中是否有可被导航到的后退项。
  • canGoForward。布尔值,指示后退列表中是否有可被导航到的前进项。
  • allowsLinkPreview。布尔值,用于确定是否按下连接可以显示链接目标的预览。
  • - goBack。导航到后退列表中的后腿项中。
  • - goForward。导航到后退列表中的前进项中。
  • - goToBackForwardListItem:。导航到后退列表中的某一个网页项,并将其设置为当前项。
  • - loadRequest:。导航到请求的URL地址。

执行JavaScript

  • - evaluateJavaScript:completionHandler:。苹果JS字符串,用于OC调用JS方法。

实例方法

  • - goBack。导航到后退列表的后退项中。
  • - goForward。导航到后退列表的前进项中。
  • - reload。重新加载当前页面。
  • - reloadFromOrigin。重新加载当前页面,如果可能,使用缓存验证条件执行端到端重新验证。
  • - stopLoading。停止加载当前页面所有资源。

WKWebViewConfiguration API

使用WKWebViewConfiguration类,你可以确定网页呈现的速度、媒体播放的处理方式等等。

WKWebViewConfiguration仅在首次初始化WebView视图的时候使用,当WebView视图被创建以后,你就无法再使用此类来更改WebView的配置信息了。

配置新的web视图的属性

  • applicationNameForUserAgent。在用户代理字符串中使用的应用程序的名称。
  • preferences。web视图要使用的首选项对象。
  • processPool。视图的web内容进程所在的进程池。
  • userContentController。与网页视图关联的用户内容控制器。
  • websiteDataStore。由网页视图使用的存储的网站数据。

确定页面可扩展性

  • ignoresViewportScaleLimites。布尔值,用于确定WKWebView是否应始终允许缩放网页。

设置渲染首选项

  • suppressesIncrementalRendering。布尔值,指示网络视图是否在【内容渲染完全加载到内存之前】禁止内容呈现,默认是NO。

设置媒体播放首选项

  • allowsInlineMediaPlayback。布尔值,指示HTML5视频是否内嵌播放,或使用native全屏控制器。
  • allowsAirPlayForMediaPlayback。是否允许AirPlay。
  • allowsPictureInPictureMediaPlayback。HTML5视图是否可以播放画中画
  • mediaTypesRequiringUserActionForPlayback。确定哪些类型需要用户手势才能播放。
  • WKAudiovisualMediaTypes。枚举类型,需要用户手势开始播放的媒体类型。

设置选择粒度

  • selectionGranularity。用户可以在网页视图中交互地选择内容的粒度级别。
  • WKSelectionGranularity。枚举类型,交互式创建和修改选择的粒度。

选择用户界面方向

  • userInterfaceDirectionPolicy。用户界面元素的方向。
  • WKUserInterfaceDirectionPolicy。枚举类型,用于确定web视图中用户界面元素的方向性策略。

识别数据类型

  • dataDetectorTypes。所需的数据监测类型。
  • WKDataDetectorTypes。枚举类型,监测到的数据类型。

WKNavigationDelegate API

WKNavigationDelegate系列的相关协议方法,监管了WebView加载前前后后的整个流程。

// 在传给WKWebView一个webUrl的时候,WebView决定是否加载该请求。比如JS与Native的通信、scheme拦截、点击电话号码是否调起拨打电话的弹窗等,都是在该代理方法中处理的。

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
}
// 收到响应后,决定是否允许或取消导航。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
{
}
//页面开始加载web内容
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation
{
}
//收到服务器重定向之后调用 (接收到服务器跳转请求)
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation
{
}
//页面加载失败时调用 (【web视图加载内容时】发生错误)
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error
{
}
//web内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation
{
}

//页面加载完成之后调用

- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
{
}

//页面加载失败时调用

- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error
{
}

//当 Web 视图需要响应认证挑战(authentication challenge)时调用。当使用 Https 协议加载web内容时,使用的证书不合法或者证书过期时需要使用该方法.

- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler
{
}

// 当Web视图的Web内容进程终止时调用,可在该函数中重新创建新的WKWebView,然后自动重新加载页面。一般而言,但WKWebView因一些稀奇古怪的原因导致Crash的时候就会回调该方法。

- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView
{
}

WKUIDelegate API

WKUIDelegate类是网页视图的用户界面委托协议,提供了代表网页呈现本机用户界面元素的方法。

WKUIDelegate的代理方法我们用到的不多,其主要是处理一些页面中常用到的JS逻辑,比如alert、confirm等。

//创建一个新的 web 视图

- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
}

//通知您的应用程序DOM窗口成功关闭。

- (void)webViewDidClose:(WKWebView *)webView
{
}

//显示一个 JavaScript 警告面板。

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
 //这里展示了如何使用OC原生来展示JS的警告弹窗
 UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"AlertPanel"message:message preferredStyle:UIAlertControllerStyleAlert];
 UIAlertAction *alertAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefaulthandler:^(UIAlertAction * _Nonnull action) {
 // ⚠️必须在这里回调 JavaScript
        completionHandler();
    }];
    [alertController addAction:alertAction];
    [self presentViewController:alertController animated:YES completion:nil];
}

//显示一个 JavaScript 确认面板。

- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler
{
}

//显示一个 JavaScript 文本输入面板。

- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler
{
}

//确定给定元素是否应显示预览。

- (BOOL)webView:(WKWebView *)webView shouldPreviewElement:(WKPreviewElementInfo *)elementInfo
{
}

//当用户执行窥视操作时调用。

- (nullable UIViewController *)webView:(WKWebView *)webView previewingViewControllerForElement:(WKPreviewElementInfo *)elementInfo defaultActions:(NSArray<id <WKPreviewActionItem>> *)previewActions
{
}

//当用户在预览中执行弹出操作时调用。

- (void)webView:(WKWebView *)webView commitPreviewingViewController:(UIViewController *)previewingViewController
{
}

//显示文件上传面板。

- (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray<NSURL *> * _Nullable URLs))completionHandler
{
}

以上就是WKWebView的一些基础的API,下面来聊聊WKWebView的基本使用。

显示加载进度条

我们可以通过监听WKWebView的estimatedProgress属性值来实现一个加载进度条,而UIWebView只能是通过timer事件做一个假的加载进度条。

给WKWebView做一个进度条的步骤如下:

1,声明并初始化一个UIProgressView

@property(nonatomic , strong) UIProgressView  *progressView;//声明进度条
//初始化进度条视图
- (UIProgressView *)progressView
{
 if (_progressView == nil) {
 CGRect frame = CGRectMake(0, XYWKNavHeight, XYWKScreenW, 5);
 _progressView = [[UIProgressView alloc] initWithFrame:frame];
 _progressView.tintColor = UIColor.blueColor;// 设置进度条色调
 _progressView.trackTintColor = kWhiteColor;//设置进度条跟踪色调
    }
 return _progressView;
}

2,监听WKWebView的estimatedProgress属性

[self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNewcontext:NULL];

3,实现- observeValueForKeyPath: ofObject:(id)object change: context:方法,并改变progressView的progress属性

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
 if ([keyPath isEqualToString:@"estimatedProgress"]) {
 if (object == self.webView) {
 if (self.shouldShowProgress) {
                [self showLoadingProgress:self.webView.estimatedProgress];
            }
        }
 else{
            [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
        }
    }
 else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}
- (void)showLoadingProgress:(CGFloat)progress{
 if (!self.progressView.superview) {
 //将进度条添加到视图上
        [self.view addSubview:self.progressView];
    }
 self.progressView.progress = progress;
 // 如果progress = 1.0 自动移除
 if(progress == 1.0f)
    {
        [self hideLoadingProgressView];
    }
}

4,移除观察者

- (void)dealloc {
 XYWKLog(@"dealloc --- %@",NSStringFromClass([selfclass]));
 if (self.shouldShowProgress) {
        [self.webView removeObserver:selfforKeyPath:@"estimatedProgress"];
    }
}

给WKWebView添加加载进度条,说白了无非就是使用KVO监听WebView的estimatedProgress属性,然后改变progressView 的progress属性值。关于KVO,我在之前的文章iOS开发中的设计模式--观察者模式中详述过。

其他

1,HTML构建各个组件,CSS给各个组件添加样式(字体大小、颜色等),JavaScript添加交互(点击响应、动画等)。

2,不管是WKWebView还是UIWebView,其实其本质都是一个能够通过网址直接获取到数据流,并将数据流解析渲染出来的组件

3,WebKit是一个开源的浏览器引擎,当前常见的浏览器基本都是基于WebKit进行延伸的。而iOS中的WebKit.framework,就是在WebCore、底层桥接、JSCore引擎等核心模块的基础上,针对iOS平台的项目封装。WKWebView就是基于WebKit.framework。

4,WKWebView的configuration属性主要是进行web前端相关业务逻辑的配置。

5,使用WKWebView的流程如下:

  1. 创建并初始化一个WKWebView
  2. 设置WebView的navigationDelegate、UIDelegate、configuration等
  3. 加载URL或者HTML字符串
  4. 在相应的代理方法回调中处理业务逻辑,

6,上面讲到,我们可以使用KVO来监听WKWebView的estimatedProgress属性值的变化来实现加载进度条,那么WKWebView中还有那些属性值的变化也是可以被KVO监听到的呢?凡是在WKWebView的属性描述中带有“is key-value observing (KVO) compliant for this property”字样的属性,都是可以通过KVO来观察到其值变化的,比如estimatedProgress、title、canGoBack、canGoForward等属性。

7,本文主要介绍了如何通过WKWebView来展示一个页面,其实,WKWebView不仅仅只有展示的功能,它还能够和Native进行交互。而且iOS中的web应用,起重点就是与Native进行交互

在iOS中,JavaScript和Native进行交互,主要是依靠JSBridge或者JavaScriptCore。JavaScriptCore是在iOS7之后推出的,之前都是使用JSBridge。我们可以通过JSCore或者JSBridge来在native中执行JS代码,并且在JS中去回调Native的相关函数

现在很火热的跨平台以及热修复技术,都是基于JS与Native通信来实现的。不管你是使用Weex、RN还是Flutter,其程序运行的终端都是iOS或者Android,我们选择JavaScript这门较为通用的语言来调动iOS或者Android,而iOS中又内嵌了JavaScriptCore这个基础组件可以实现JS与Native之间的通信。很多跨平台技术,其核心就是利用了JS与Native的通信技术。

以上。

原文发布于微信公众号 - iOS小生活(iOSHappyLife)

原文发表时间:2019-06-01

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券