在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拦截、点击电话号码是否调起拨打电话的弹窗等,都是在该代理方法中处理的。
代码语言:javascript复制- (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
{
}
//页面加载完成之后调用
代码语言:javascript复制- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
{
}
//页面加载失败时调用
代码语言:javascript复制- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error
{
}
//当 Web 视图需要响应认证挑战(authentication challenge)时调用。当使用 Https 协议加载web内容时,使用的证书不合法或者证书过期时需要使用该方法.
代码语言:javascript复制- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler
{
}
// 当Web视图的Web内容进程终止时调用,可在该函数中重新创建新的WKWebView,然后自动重新加载页面。一般而言,但WKWebView因一些稀奇古怪的原因导致Crash的时候就会回调该方法。
代码语言:javascript复制- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView
{
}
WKUIDelegate API
WKUIDelegate类是网页视图的用户界面委托协议,提供了代表网页呈现本机用户界面元素的方法。
WKUIDelegate的代理方法我们用到的不多,其主要是处理一些页面中常用到的JS逻辑,比如alert、confirm等。
//创建一个新的 web 视图
代码语言:javascript复制- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
}
//通知您的应用程序DOM窗口成功关闭。
代码语言:javascript复制- (void)webViewDidClose:(WKWebView *)webView
{
}
//显示一个 JavaScript 警告面板。
代码语言: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 确认面板。
代码语言:javascript复制- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler
{
}
//显示一个 JavaScript 文本输入面板。
代码语言:javascript复制- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler
{
}
//确定给定元素是否应显示预览。
代码语言:javascript复制- (BOOL)webView:(WKWebView *)webView shouldPreviewElement:(WKPreviewElementInfo *)elementInfo
{
}
//当用户执行窥视操作时调用。
代码语言:javascript复制- (nullable UIViewController *)webView:(WKWebView *)webView previewingViewControllerForElement:(WKPreviewElementInfo *)elementInfo defaultActions:(NSArray<id <WKPreviewActionItem>> *)previewActions
{
}
//当用户在预览中执行弹出操作时调用。
代码语言:javascript复制- (void)webView:(WKWebView *)webView commitPreviewingViewController:(UIViewController *)previewingViewController
{
}
//显示文件上传面板。
代码语言:javascript复制- (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
代码语言:javascript复制@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属性
代码语言:javascript复制[self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNewcontext:NULL];
3,实现- observeValueForKeyPath: ofObject:(id)object change: context:方法,并改变progressView的progress属性
代码语言:javascript复制- (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,移除观察者
代码语言:javascript复制- (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的流程如下:
- 创建并初始化一个WKWebView
- 设置WebView的navigationDelegate、UIDelegate、configuration等
- 加载URL或者HTML字符串
- 在相应的代理方法回调中处理业务逻辑,
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的通信技术。
以上。