注册

WebKit的使用

Web view 用于加载和显示丰富的网络内容。例如,嵌入 HTML 和网站。Mail app 使用 web view 显示邮件中的 HTML 内容。

1dabe4cabd32806b107ea3742487a58d.png

iOS 8 和 macOS 10.10 中引入了WebKit framework,用以取代UIWebView和WebView。同时,在两个平台上提供同一API。与UIWebView相比,WebKit 有以下优势:使用了与 Safari 一样的 JavaScript engine,在运行脚本前,将脚本编译为机器代码,速度更快;支持多进程架构,Web 内容在单独的线程中运行,WKWebView崩溃不会影响 app运行;能够以60fps滑动。另外,在 iOS 12 和 macOS 10.14中,UIWebView和WebView已经被正式弃用。

WebKit framework 提供了多个类和协议,用于在窗口中显示网络内容,并实现类似浏览器功能。例如,点击链接时显示链接内容,维护前进、后退列表,维护最近访问列表。加载网页内容时,异步从 HTTP 服务器请求内容,其响应 (response) 可能以增量、随机顺序到达,也可能因网络原因部分到达,而 WebKit 极大简化了这些过程。WebKit 框架还简化了显示各种 MIME 类型内容过程,以及管理视图中各元素滚动条。

WebKit 框架中的方法、函数只能在主线程或主队列中调用。

1. WKWebView
WKWebView是 WebKit framework 的核心。在 app 内使用WKWebView插入网络内容步骤如下:

1、创建WKWebView对象。
2、将WKWebView设置为要显示的视图。
3、向WKWebView发送加载 Web 内容的请求。

使用initWithFrame:configuration:方法创建WKWebView,使用loadHTMLString:baseURL:加载本地HTML文件,或使用loadRequest:方法加载网络内容。如下:

// Local HTMLs
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:self.webConfiguration];
self.view = webView;
NSURL *htmlURL = [[NSBundle mainBundle] URLForResource:@"WKWebView - NSHipster" withExtension:@"htm"];
NSURL *baseURL = [htmlURL URLByDeletingLastPathComponent];
NSString *htmlString = [NSString stringWithContentsOfURL:htmlURL
encoding:NSUTF8StringEncoding
error:NULL];
[webView loadHTMLString:htmlString baseURL:baseURL];

// Web Content
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:self.webConfiguration];
self.view = webView;
NSURL *myURL = [NSURL URLWithString:@"https://github.com/pro648/tips/wiki"];
NSURLRequest *request = [NSURLRequest requestWithURL:myURL
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:30];
[webView loadRequest:request];

本地HTML文件可以在demo源码中获取:https://github.com/pro648/BasicDemos-iOS/tree/master/WebKit

如图所示:

d2061cbaa2da287a9b11662d7ad58de9.png

31bc6c45a608bb7b1d70cb754177db48.png

设置allowsBackForwardNavigationGestures属性可以开启、关闭横向滑动触发前进、后退导航功能:

self.webView.allowsBackForwardNavigationGestures = YES;

1.1 KVO

WKWebView中的title、URL、estimatedProgress、hasOnlySecureContent和loading

属性支持键值观察,可以通过添加观察者,获得当前网页标题、加载进度等。

根据文档serverTrust属性也支持KVO,但截至目前,在iOS 12.1 (16B91)中使用观察者观察该属性,运行时会抛出this class is not key value coding-compliant for the key serverTrust的异常。

将网页标题显示出来可以帮助用户了解当前所在位置,显示当前导航进度能够能够让用户感受到加载速度,另外,还可以观察hasOnlySecureContent查看当前网页所有资源是否均通过加密连接传输。在viewDidLoad中添加以下代码:

[self.webView addObserver:self forKeyPath:@"hasOnlySecureContent" options:NSKeyValueObservingOptionNew context:webViewContext];
[self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:webViewContext];
[self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:webViewContext];

实现observerValueForKeyPath:ofObject:change:context:方法,在观察到值变化时进行对应操作:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"hasOnlySecureContent"]) {
BOOL onlySecureContent = [[change objectForKey:NSKeyValueChangeNewKey] boolValue];
NSLog(@"onlySecureContent:%@",onlySecureContent ? @"YES" : @"NO");
} else if ([keyPath isEqualToString:@"title"]) {
self.navigationItem.title = change[NSKeyValueChangeNewKey];
} else if ([keyPath isEqualToString:@"estimatedProgress"]) {
self.progressView.hidden = [change[NSKeyValueChangeNewKey] isEqualToNumber:@1];

CGFloat progress = [change[NSKeyValueChangeNewKey] floatValue];
self.progressView.progress = progress;
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}

如果你对键值观察、键值编码还不熟悉,可以查看我的另一篇文章:KVC和KVO学习笔记

运行demo,如下所示:

18504472edc41aaef9e5ea9eba99ae2e.gif

控制台会输出如下内容:

onlySecureContent:YES

WKWebView调用reload、stopLoading、goBack、goForward可以实现刷新、返回、前进等功能:

- (void)refreshButtonTapped:(id)sender {
[self.webView reload];
}

- (void)stopLoadingButtonTapped:(id)sender {
[self.webView stopLoading];
}

- (IBAction)backButtonTapped:(id)sender {
[self.webView goBack];
}

- (IBAction)forwardButtonTapped:(id)sender {
[self.webView goForward];
}

还可以通过观察loading属性,在视图加载完成时,更新后退、前进按钮状态:

- (void)viewDidLoad {
...

[self.webView addObserver:self forKeyPath:@"loading" options:NSKeyValueObservingOptionNew context:webViewContext];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
...
if (context == webViewContext && [keyPath isEqualToString:@"loading"]) {
BOOL loading = [change[NSKeyValueChangeNewKey] boolValue];
// 加载完成后,右侧为刷新按钮;加载过程中,右侧为暂停按钮。
self.navigationItem.rightBarButtonItem = loading ? self.stopLoadingButton : self.refreshButton;

self.backButton.enabled = self.webView.canGoBack;
self.forwardButton.enabled = self.webView.canGoForward;
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}

1.2 截取网页视图

在iOS 11和macOS High Sierra中,WebKit framework增加了takeSnapshotWithConfiguration:completionHandler: API用于截取网页视图。截取网页可见部分视图方法如下:

- (IBAction)takeSnapShot:(UIBarButtonItem *)sender {
WKSnapshotConfiguration *shotConfiguration = [[WKSnapshotConfiguration alloc] init];
shotConfiguration.rect = CGRectMake(0, 0, self.webView.bounds.size.width, self.webView.bounds.size.height);

[self.webView takeSnapshotWithConfiguration:shotConfiguration
completionHandler:^(UIImage * _Nullable snapshotImage, NSError * _Nullable error) {
// 保存截图至相册,需要在info.plist中添加NSPhotoLibraryAddUsageDescription key和描述。
UIImageWriteToSavedPhotosAlbum(snapshotImage, NULL, NULL, NULL);
}];
}

此前,截取网页视图需要结合图层和graphics context。现在,只需要调用单一API。

1.3 执行JavaScript

可以使用evaluateJavaScript:completionHandler:方法触发web view JavaScript。下面方法触发输出web view userAgent:

[self.webView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id _Nullable userAgent, NSError * _Nullable error) {
NSLog(@"%@",userAgent);
}];

在iOS 12.1.2 (16C101) 中,输出如下:

Mozilla/5.0 (iPhone; CPU iPhone OS 12_1_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/16C101

2. WKWebViewConfiguration

WKWebViewConfiguration是用于初始化Web视图属性的集合。通过WKWebViewConfiguration类,可以设置网页渲染速度,视频是否自动播放,HTML5 视频是否一帧一帧播放,如何与本地代码通信等。

WKWebViewConfiguration属性有偏好设置preference、线程池processPool和用户内容控制器userContentController等。

Web view 初始化时才需要WKWebViewConfiguration对象,WKWebView创建后无法修改其configuration。多个WKWebView可以使用同一个configuration。

648ded90f1858494f56506abd915f917.png

例如,设置网页中最小字体为30,自动检测电话号码:

- (WKWebViewConfiguration *)webConfiguration {
if (!_webConfiguration) {
_webConfiguration = [[WKWebViewConfiguration alloc] init];

// 偏好设置 设置最小字体
WKPreferences *preferences = [[WKPreferences alloc] init];
preferences.minimumFontSize = 30;
_webConfiguration.preferences = preferences;

// 识别网页中的电话号码
_webConfiguration.dataDetectorTypes = WKDataDetectorTypePhoneNumber;

// Web视图内容完全加载到内存之前,禁止呈现。
_webConfiguration.suppressesIncrementalRendering = YES;
}
return _webConfiguration;
}

suppressesIncrementalRendering属性是布尔值,决定Web视图内容在完全加载到内存前是否显示,默认为NO,即边加载边显示。例如,Web视图中有文字和图片,会先显示文字后显示图片。

3. Scripts

用户脚本 (User Scripts) 是文档开始加载或加载完成后注入 Web 页面的 JS。User Scripts非常强大,其能够通过客户端设置网页,允许注入事件监听器,甚至可以注入脚本,这些脚本又可以回调 native app 。

3.1 WKUserScript

WKUserScript对象表示可以注入网页的脚本。initWithSource:injectionTime:forMainFrameOnly:方法返回可以添加到userContentController控制器的脚本。其中,source 参数为 script 源码;injectionTime为WKUserScriptInjectionTimeAtDocumentStart、WKUserScriptInjectionTimeAtDocumentEnd,

其参数如下:

source: script 源码。
injectionTime: user script注入网页时间,为WKUserScriptInjectionTime枚举常量。WKUserScriptInjectionTimeAtDocumentStart在创建文档元素之后,加载任何其他内容之前注入。WKUserScriptInjectionTimeAtDocumentEnd在加载文档后,但在加载其他子资源之前注入。
forMainFrameOnly: 布尔值,YES时只注入main frame,NO时注入所有 frame。
下面代码将隐藏 Wikipedia toc、mw-panel 脚本注入网页,同时使用 JS 提取网页 toc 表格内容:

// 隐藏wikipedia左边缘和contents表格
NSURL *hideTableOfContentsScriptURL = [[NSBundle mainBundle] URLForResource:@"hide" withExtension:@"js"];
NSString *hideTableOfContentsScriptString = [NSString stringWithContentsOfURL:hideTableOfContentsScriptURL
encoding:NSUTF8StringEncoding error:NULL];
WKUserScript *hideTableOfContentsScript = [[WKUserScript alloc] initWithSource:hideTableOfContentsScriptString
injectionTime:WKUserScriptInjectionTimeAtDocumentStart
forMainFrameOnly:YES];

// 获取contents表格内容
NSString *fetchTableOfContentsScriptString = [NSString stringWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"fetch" withExtension:@"js"] encoding:NSUTF8StringEncoding error:NULL];
WKUserScript *fetchTableOfContentsScript = [[WKUserScript alloc] initWithSource:fetchTableOfContentsScriptString
injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
forMainFrameOnly:YES];

本文中的 js 和 HTML 均可通过文章底部源码链接获取。要使用 JavaScript 提取内容的网页为:https://en.wikipedia.org/w/index.php?title=San_Francisco&mobileaction=toggle_view_desktop

3.2 WKUserContentController
WKUserContentController对象为 JavaScript 提供了发送消息至 native app,将 user scripts 注入 Web 视图方法。

将 user script 添加到userContentController才可以注入网页中:

WKUserContentController *userContentController = [[WKUserContentController alloc] init];
[userContentController addUserScript:hideTableOfContentsScript];
[userContentController addUserScript:fetchTableOfContentsScript];

要监听 JavaScript 消息,需要先注册要监听消息名称。添加监听事件方法为addScriptMessageHandler:name:,参数如下:

scriptMessageHandler: 处理监听消息,该类需要遵守WKMessageHandler协议。
name: 要监听消息名称。
使用该方法添加监听事件后,JavaScript 的 window.webkit.messageHandlers.name.postMessage(messageBody) 函数将被定义在使用了该userContentController网页视图的所有frame。

监听fetch.js中 didFetchTableOfContents 消息:

[userContentController addScriptMessageHandler:self name:@"didFetchTableOfContents"];

// 最后,将userContentController添加到WKWebViewConfiguration
_webConfiguration.userContentController = userContentController;

3.3 WKScriptMessageHandler

监听 script message 的类必须遵守WKMessageHandler协议,实现该协议唯一且必须实现的userContentController:didReceiveScriptMessage:方法。Webpage 接收到脚本消息时会调用该方法。

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if ([message.name isEqualToString:@"didFetchTableOfContents"]) {
id body = message.body;
if ([body isKindOfClass:NSArray.class]) {
NSLog(@"messageBody:%@",body);
}
}
}

如下所示:

ce231809cd2c8c36532b5c66bdf7998c.gif

JavaScript 消息是WKScriptMessage对象,该对象属性如下:

body:消息内容,可以是NSNumber、NSString、NSDate、NSArray、NSDictionary、NSNull类型。
frameInfo:发送该消息的frame。
name:接收消息对象名称。
webView:发送该消息的网页视图。
最终,我们成功的将事件从 iOS 转发到 JavaScript,并将 JavaScript 转发回 iOS。

4. WKNavigationDelegate

用户点击链接,使用前进、后退手势,JavaScript 代码(例如,window.location = ' https://github.com/pro648 '),使用代码调用loadRequest:等均会让网页加载内容,即action引起网页加载;随后,web view 会向服务器发送request,接收response,可能会是positive response,也可能请求失败;之后接收数据。我们的应用可以在action后、request前,或者response后、data前自定义网页加载,决定继续加载,或取消加载。

aad76d07978f6c15dec7d7a7dc39d90c.png

WKNavigationDelegate协议内方法可以自定义Web视图接收、加载和完成导航请求过程的行为。

首先,声明遵守WKNavigationDelegate协议:

@interface ViewController () <WKNavigationDelegate>

其次,指定遵守WKNavigationDelegate协议的类为 web view 代理:

self.webView.navigationDelegate = self;

最后,根据需要实现所需WKNavigationDelegate方法。

webView:decidePolicyForNavigationAction:decisionHandler:方法在action后响应,webView:decidePolicyForNavigationResponse:decisionHandler:方法在response后响应。

根据前面的配置,WKWebView会自动识别网页中电话号码。截至目前,电话号码只能被识别,无法点击。可以通过实现webView:decidePolicyForNavigationAction:decisionHandler:方法,调用系统Phone app拨打电话

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
if (navigationAction.navigationType == WKNavigationTypeLinkActivated && [navigationAction.request.URL.scheme isEqualToString:@"tel"]) {
[UIApplication.sharedApplication openURL:navigationAction.request.URL options:@{} completionHandler:^(BOOL success) {
NSLog(@"Successfully open url:%@",navigationAction.request.URL);
}];
decisionHandler(WKNavigationActionPolicyCancel);
} else {
decisionHandler(WKNavigationActionPolicyAllow);
}
}

实现了该方法后,必须调用decisionHandler块。该块参数为WKNavigationAction枚举常量。WKNavigationActionPolicyCancel取消导航,WKNavigationActionPolicyAllow继续导航。

WKNavigationAction对象包含引起本次导航的信息,用于决定是否允许本次导航。其属性如下:

request:本次导航的request。
sourceFrame:WKFrameInfo类型,请求本次导航frame信息。
targetFrame:目标frame。如果导航至新窗口,则targetFrame为nil。
navigationType:WKNavigationType枚举类型,为以下常量:
WKNavigationTypeLinkActivated:用户点击href链接。
WKNavigationTypeFormSubmitted:提交表格。
WKNavigationTypeBackForward:请求前进、后退列表中item。
WKNavigationTypeReload:刷新网页。
WKNavigationTypeFormResubmitted:因后退、前进、刷新等重新提交表格。
WKNavigationTypeOther:其他原因。
WKFrameInfo对象包含了一个网页中的frame信息,其只是一个描述瞬时状态 (transient) 的纯数据 (data-only) 对象,不能在多次消息调用中唯一标志某个frame。

如果需要在response后操作导航,需要实现webView:decidePolicyForNavigationResponse:decisionHandler:方法。WKNavigationResponse对象包含navigation response信息,用于决定是否接收response。其属性如下:

canShowMIMEType:布尔类型值,指示WebKit是否显示MIME类型内容。
forMainFrame:布尔类型值,指示即将导航至的frame是否为main frame。
response:NSURLResponse类型。
实现了该方法后,必须调用decisionHandler块,否则会在运行时抛出异常。decisionHandler块参数为WKNavigationResponsePolicy枚举类型。WKNavigationResponseCancel取消导航,WKNavigationResponseAllow继续导航。

Navigation action 和 navigation response 既可以在处理完毕后立即调用decisionHandler,也可以异步调用。

5. WKUIDelegate

WKWebView与 Safari 类似,尽管前者在一个窗口显示内容。如果需要打开多个窗口、监控打开、关闭窗口,修改用户点击元素时显示哪些选项,需要使用WKUIDelegate协议。

首先,声明遵守WKUIDelegate协议:

@interface ViewController () <WKUIDelegate>

其次,指定遵守WKUIDelegate协议的类为 web view 代理:

self.webView.uiDelegate = self;

最后,根据需要实现WKUIDelegate协议内方法。

5.1 新窗口打开

如何响应 JavaScript 的打开新窗口函数、或target="_blank"标签?有以下三种方法:

创建新的WKWebView,并在新的页面打开。
在 Safari 浏览器打开。
捕捉 JS ,在同一个WKWebView加载。
当 URL 为 mail、tel、sms 和 iTunes链接时交由系统处理。此时,系统会交由对应 app 处理。其他情况在当前 web view 加载。

5.2 响应 JavaScript 弹窗
在响应 JavaScript 时,可以通过WKUIDelegate协议使用 native UI呈现,有以下三种方法:

1、webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler::为了用户安全,在该方法的实现中需要需要标志出提供当前内容的网址,最为简便的方法便是frame.request.URL.host,响应面板应只包含一个OK按钮。当 alert panel 消失后,调用completionHandler。
2、webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler::为了用户安全,在该方法的实现中需要需要标志出提供当前内容的网址,最为简便的方法便是frame.request.URL.host,响应面板包括两个按钮,一般为OK和Cancel。当 alert panel 消失后,调用completionHandler。如果用户点击的是OK按钮,为completionHandler传入YES;如果用户点击的是Cancel按钮,为completionHandler传入NO。
3、webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler::为了用户安全,在该方法的实现中需要需要标志出提供当前内容的网址,最为简便的方法便是frame.request.URL.host,响应面板包括两个按钮(一个OK按钮,一个Cancel按钮)和一个输入框。当面板消失时调用completionHandler,如果用户点击的是OK按钮,传入文本框文本;否则,传入nil。

例如,输入账号、密码前点击登陆按钮,大部分网页会弹出警告框。在 JavaScript 中,会弹出 alert 或 confirm box。

JavaScript alert 会堵塞当前进程,调用completionHandler后 JavaScript 才会继续执行。

6. WKURLSchemeHandler

UIWebView支持自定义NSURLProtocol协议。如果想要加载自定义 URL 内容,可以通过创建、注册NSURLProtocol子类实现。此后,任何调用自定义 scheme (例如,hello world://) 的方法,都会调用NSURLProtocol子类,在NSURLProtocol子类处理自定义 scheme ,这将非常实用。例如,在 book 中加载图片、视频等。

WKWebView不支持NSURLProtocol协议,因此不能加载自定义 URL Scheme。在 iOS 11 中,Apple 为 WebKit framework 增加了WKURLSchemeHandler协议,用于加载自定义 URL Scheme。

WebKit遇到无法识别的 URL时,会调用WKURLSchemeHandler协议。该协议包括以下两个必须实现的方法:

webView:startURLSchemeTask::加载资源时调用。
webView:stopURLSchemeTask::WebKit 调用该方法以终止 (stop) 任务。调用该方法后,不得调用WKURLSchemeTask协议的任何方法,否则会抛出异常。
使用WKURLSchemeHandler协议处理完毕任务后,调用WKURLSchemeTask协议内方法加载资源。WKURLSchemeTask协议包括request属性,该属性为NSURLRequest类型对象。还包含以下方法:

didReceiveResponse::设置当前任务的 response。每个 task 至少调用一次该方法。如果尝试在任务终止或完成后调用该方法,则会抛出异常。
didReceiveData::设置接收到的数据。当接收到任务最后的 response 后,使用该方法发送数据。每次调用该方法时,新数据会拼接到先前收到的数据中。如果尝试在发送 response 前,或任务完成、终止后调用该方法,则会引发异常。
didFinish:将任务标记为成功完成。如果尝试在发送 response 前,或将已完成、终止的任务标记为完成,则会引发异常。
didFailWithError::将任务标记为失败。如果尝试将已完成、失败,终止的任务标记为失败,则会引发异常。
在WKURLSchemeHandler协议方法内,可以获取到请求的request。因此,可以提取 URL 中任何内容,并将数据转发给WKWebView进行加载。

下面的方法分别使用 url 、custom URL Scheme加载网络图片和相册图片:

- (void)webView:(WKWebView *)webView startURLSchemeTask:(id<WKURLSchemeTask>)urlSchemeTask {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
NSURL *url = urlSchemeTask.request.URL;
if ([url.absoluteString containsString:@"custom-scheme"]) {
NSArray<NSURLQueryItem *> *queryItems = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:YES].queryItems;
for (NSURLQueryItem *item in queryItems) {

// example: custom-scheme://path?type=remote&url=https://placehold.it/120x120&text=image1
if ([item.name isEqualToString:@"type"] && [item.value isEqualToString:@"remote"]) {
for (NSURLQueryItem *queryParams in queryItems) {
if ([queryParams.name isEqualToString:@"url"]) {
NSString *path = queryParams.value;
path = [path stringByReplacingOccurrencesOfString:@"\\" withString:@""];

// 获取图片
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:path] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
[urlSchemeTask didReceiveResponse:response];
[urlSchemeTask didReceiveData:data];
[urlSchemeTask didFinish];
}];
[task resume];
}
}
} else if ([item.name isEqualToString:@"type"] && [item.value isEqualToString:@"photos"]) { // example: custom-scheme://path?type=photos
dispatch_async(dispatch_get_main_queue(), ^{
self.imagePicker = [[ImagePicker alloc] init];
[self.imagePicker showGallery:^(BOOL flag, NSURLResponse * _Nonnull response, NSData * _Nonnull data) {
if (flag) {
[urlSchemeTask didReceiveResponse:response];
[urlSchemeTask didReceiveData:data];
[urlSchemeTask didFinish];
} else {
NSError *error = [NSError errorWithDomain:urlSchemeTask.request.URL.absoluteString code:0 userInfo:NULL];
[urlSchemeTask didFailWithError:error];
}
}];
});
}
}
}
});
}

- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id<WKURLSchemeTask>)urlSchemeTask {
NSError *error = [NSError errorWithDomain:urlSchemeTask.request.URL.absoluteString code:0 userInfo:NULL];
[urlSchemeTask didFailWithError:error];
}

实现上述方法的类必须遵守WKURLSchemeHandler协议。另外,必须在WKWebView的配置中注册所支持的 URL Scheme:

// 添加要自定义的url scheme
[_webConfiguration setURLSchemeHandler:self forURLScheme:@"custom-scheme"];

运行如下:

fe46cfb9173351efc31785f5bd65d6ab.gif

总结

WebKit为 iOS 、macOS 开发人员提供了一套强大的开发工具,可以直接在 app 网页视图中操作 JavaScript,使用 user script 将 JavaScript 注入网页,使用WKScriptMessageHandler协议接收 JavaScript 消息。使用WKNavigationDelegate协议自定义网页导航,使用WKUIDelegate在网页上呈现 native UI,使用WKURLSchemeHandler加载自定义 URL Scheme 内容。

如果只是简单呈现网页视图,推荐使用 iOS 9 推出的SFSafariViewController,几行代码就可实现与 Safari 一样的体验。SFSafariViewController还提供了自动填充、欺诈网站监测等功能。

Demo名称:WebKit
源码地址:https://github.com/pro648/BasicDemos-iOS/tree/master/WebKit

链接:https://www.jianshu.com/p/65c66f924d56

0 个评论

要回复文章请先登录注册