我是一名iOS开发者,我是一名非计算机专业出生的程序员。
如果学习很幸苦,那就尝试无知的代价
WKWebView 如果需要个性化配置,则应该使用以下方法进行初始化。
- (instancetype)initWithFrame:(CGRect)frame
configuration:(WKWebViewConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
示例代码:
WKWebViewConfiguration *conf = [WKWebViewConfiguration new];
WKWebView *wk = [[WKWebView alloc] initWithFrame:CGRectZero configuration:conf];
WKWebView 网页的导航代理,可以理解为网页的生命周期事件循环。
WKWebView 网页的UI交互代理,对于JS中的 UI 类型操作需要实现对应的方法,例如 window.alert
、window.confirm
等操作。
WKWebView 系统默认支持对网页历史记录的管理,经过实际测试 302状态码的网页请求不属于历史记录,200状态码的网页请求属于正常的历史记录。
支持仅对历史记录列表和数据的获取
WKBackForwardListItem 为每一项历史记录的数据模型。
/*! @abstract The URL of the webpage represented by this item.
*/
@property (readonly, copy) NSURL *URL;
/*! @abstract The title of the webpage represented by this item.
*/
@property (nullable, readonly, copy) NSString *title;
/*! @abstract The URL of the initial request that created this item.
*/
@property (readonly, copy) NSURL *initialURL;
其中 initialURL 和 URL 的区别:
initialURL
为本次网页加载的初始请求URL
为本次网页加载结束后的最终请求title
为本次网页加载结束时的 <title></title>
标签正常情况下,一般用 loadRequest 方法加载即可。
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
loadData 和 loadHTML 也能加载网络地址,原理都是通过先获取 NSData 后,利用该方法加载,但是要注意获取 NSData 的过程是同步,如果网络请求较慢,会造成主线程阻塞。
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
// 直接加载H5数据
// [wk loadHTMLString:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] baseURL:nil];
[wk loadData:data MIMEType:@"text/html" characterEncodingName:@"UTF-8" baseURL:nil];
loadHTMLString
和 loadData
中的参数说明:
baseURL
:会影响网页加载过程中css、js、图片等资源文件的相对路径,并不会影响绝对路径。
MIMEType
:为支持加载网页的类型,有如下类型
文件拓展名 | MIMEType |
---|---|
png | image/png |
bmp/dib | image/bmp |
jpg/jpeg/jpg | image/ipeg |
gif | image/gif |
mp3 | audio/mpeg |
mp4/mpg4/m4v/mp4v | video/mp4 |
js | application/javascript |
application/pdf | |
text/txt | text/plain |
json | application/json |
xml | text/xml |
characterEncodingName
:当前返回信息的数据编码格式:UTF-8、UTF-16、UTF-32、GBK、GB2312等,一般使用 UTF-8。
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL
allowingReadAccessToURL:(NSURL *)readAccessURL API_AVAILABLE(macos(10.11), ios(9.0));
readAccessURL
参数不能为空,否则会造成应用崩溃。readAccessURL
参数:允许访问的资源路径,如果是在 Bundle
中加载本地 HTML
,则需要设置该HTML
所在的Bundle
路径,路径设置好后,该目录在WKWebView中被视为沙盒目录,HTML
就可以访问同级目录下的资源文件。HTML主地址应为:
YOUR_APP_PATH
/WKBundle.bundle/sandbox/index.html
readAccessURL应为以下两种:
YOUR_APP_PATH
/WKBundle.bundle/sandbox YOUR_APP_PATH
/WKBundle.bundle/readAccessURL
的参数为当前HTML所在目录允许访问,该参数对Bundle目录影响不大,对沙盒目录影响较大。这个参数不可以设置为:YOUR_APP_PATH
/WKBundle.bundle/sandbox/js,否则会造成访问出错。
其余访问本地HTML的方法
[wk loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath:indexPath]]];
同样,上文中的 loadData 和 loadHTML 也可以访问本地html,同样也存在同步阻塞的问题。
Docuemnt
、Library
和 tmp
目录
如果本地HTML放置在 Docuemnt ,Library 下的话, 则需要将目录设置为所有静态资源的最外层。 例如目录结构为:
├─ html-demo
| ├─ common
| | ├─ index.css
| | └─ index.js
| ├─ pages
| | ├─ relative-common
| | | ├─ index.css
| | | └─ index.js
| | └─ index.html
如果将 html-demo 目录放置在 Document、Library 目录下
通过 [WKWebView loadFileURL:allowingReadAccessToURL:]
方法可以访问当前目录下的相对资源,还需要设置指定访问 allowingReadAccessToURL
下的资源,注意 allowingReadAccessToURL
需要设置 html
和 css
同时存在的最外层目录,如上例中,我们将 allowingReadAccessToURL
所需参数设置为 html-demo
目录的话,这样既能访问 common
目录下的资源,也能访问 relative-common
下的资源。
通过 [WKWebView loadRequest:]
方法访问 index.html
的话,仅能访问当前页面所在目录下的相对路径资源,无法访问目录外的资源,例如上例中,index.html
仅能访问 relative-common
目录下的资源,不能访问 common
目录下的资源。
通过 [WKWebView loadData:]
和 [WKWebView loadHTMLString:]
方法仅能加载当前HTML内容,无法加载资源文件,这种加载模式下,由于不需要访问其他路径下的资源,属于单页渲染和加载,所以效率高。
如果将 html-demo 目录放置在 tmp 目录下
通过 [WKWebView loadRequest:]
方法访问 index.html
的话,既能访问 common
下的资源,也能访问 relative-common
下的资源。
其余规则同上。
title
: 网页的标题,一般为 html
中的 <title></title>
中的内容URL
: 网页的URL地址,为最终加载的地址loading
: 网页是否处于加载中,YES 加载中、 NO 加载完成estimatedProgress
: 网页加载进度hasOnlySecureContent
: 网页上的所有资源是否已通过https加载serverTrust
: 加载HTTPS请求服务端所信任的证书以上属性都可以采用 KVO 观察属性变化:
// NSKeyValueObservingOptionNew 更改后的值
// NSKeyValueObservingOptionOld 更改前的值
// NSKeyValueObservingOptionInitial 观察初始化的值(在注册观察服务时会调用一次触发方法)
// NSKeyValueObservingOptionPrior 分别在值修改前后触发方法(即一次修改有两次触发)
[wk addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionInitial context:NULL];
注意:添加观察者模式后,一定要在合适的时机将观察者模式移除,否则在 iOS10 以下的设备会造成崩溃,原因是WKWebView 在释放的时候,扔被强持有观察者。
allowsBackForwardNavigationGestures
: 允许手势交互进行页面导航跳转customUserAgent
: 自定义Web页面的UserAgent,会覆盖容器原有的 User-Agent
请求头信息allowsLinkPreview
: 允许 3Dtouch 预览页面,压力屏存在的情况下webView 方法
canGoBack
: 是否可以返回上一页canGoForward
: 是否可以前进一页goBack
: 返回上一页goForward
: 前进一页eload
: 根据当前URL刷新页面reloadFromOrigin
: 根据最初URL刷新页面stopLoading
: 停止加载evaluateJavaScript
: 执行一段js代码handlesURLScheme:(NSString *)urlScheme
: 拦截自定义请求协议,不允许拦截 http,https,ws,wsstakeSnapshotWithConfiguration
: 截图配置 https://www.jianshu.com/p/67146af7ef50,只能截取当前一屏画面WKProcessPool
用于提供给 WKWebView
获取Web内容的进程池,里面包括 cookie
。当一个WebView 初始化,一个新的 Web 内容进程会从一个特殊的进程池中创建,或者一个已存在的进程会被使用。
WKProcessPool
本身没有任何方法和属性,通过实现单例进程池后,可以达到 WKWebView
间共享 cookie
的能力,注意:如果在账户退出登录后,单例进程需要释放。
WKWebView 的偏好设置,支持以下设置:
minimumFontSize
: 最小字体设置,默认为 0, H5
中 css
的 “font-size”
的值如果小于该值,则会使用该值作为字体的最小尺寸。javaScriptEnabled
: 是否启用 js
脚本,默认启用,关闭则不会运算 js
脚本,加快渲染速度。javaScriptCanOpenWindowsAutomatically
: 使用允许使用 js
自动打开 Window
,默认不允许,js
在调用 window.open
方法的时候,必须将改值设置为 YES
,才能从 WKUIDelegate
的代理方法中获取到。WKUserScript *userScript = [[WKUserScript alloc] initWithSource:@"window.open('https://www.baidu.com')"
injectionTime:(WKUserScriptInjectionTimeAtDocumentStart)
forMainFrameOnly:YES];
属性解释:
injectionTime
: js
代码的注入时机,支持 WKUserScriptInjectionTimeAtDocumentStart ,WKUserScriptInjectionTimeAtDocumentEND,分别代表页面刚渲染前执行,和页面渲染后执行。forMainFrameOnly
: 是否仅注入在主框架,还是包括所有的 iframe
全部注入。使用 addUserScript
方法来添加 js
脚本。
如果注入时机为在网页渲染前,那么网页加载完毕后执行移除脚本操作,则脚本的运算结果并不会受影响,但是在网页加载完毕前移除脚本的后,脚本将不会执行。
用于 Native 和 js 通信,需要实现 WKScriptMessageHandler 协议。
WKUserContentController *userController = [[WKUserContentController alloc] init];
[userController addScriptMessageHandler:self name:@"JSBridge"];
[userController addScriptMessageHandler:self name:@"HWH5"];
conf.userContentController = userController;
JS代码
window.webkit.messageHandlers.JSBridge.postMessage(...args)
window.webkit.messageHandlers.HWH5.postMessage(...args)
WKScriptMessageHandler
协议,并实现 didReceiveScriptMessage
方法接受消息,通过 message.name
区分不通的协议- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
NSLog(@"%@", message.name);
}
根据脚本消息通道名称移除对应的脚本消息通道。可以在任何时机移除,移除后对应的js代码也会移除。
该配置需要结合内容过滤器编译一起使用,通过对 js 指定的规则编译后得到一个 WKContentRuleList
,并且通过 userController
添加进 WebView
中。
以下情况中,WKWebView 在主动发送请求时不会携带 cookie 。
可以使用 iOS11 的新API对 WKWebView 进行 cookie 的设置,利用以下代码对 Cookie 进行持久化设置
NSHTTPCookie *cookie = ....;
[[WKWebsiteDataStore defaultDataStore].httpCookieStore setCookie:cookie ...];
示例代码:
WKWebViewConfiguration *conf = [WKWebViewConfiguration new];
// 在初始化方法之前,设置 cookie
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithPropertie:...];
[[WKWebsiteDataStore defaultDataStore].httpCookieStore setCookie:cookie ...];
WKWebView *wk = [[WKWebView alloc] initWithFrame:CGRectZero configuration:conf];
[wk loadRequest:...];
WKWebViewConfiguration *conf = [WKWebViewConfiguration new];
WKWebView *wk = [[WKWebView alloc] initWithFrame:CGRectZero configuration:conf];
// 在初始化方法之后,设置 cookie
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithPropertie:...];
[[WKWebsiteDataStore defaultDataStore].httpCookieStore setCookie:cookie ...];
[wk loadRequest:...];
是否等待H5内容全部加载完成后才开始渲染画面,默认为 NO
,如果设置为 YES
,则 H5
在加载完成之前一直处于白屏状态。
例如H5代码:
// 在测试H5页面尾巴处加入如下代码,可以查看区别。
<script type="text/javascript">
setTimeout(function(){
for(var i = 1; i<10000; i++)
{
alert(i);
}
})
</script>
不会覆盖原来的请求头重 User-Agent
的值属性,在之后追加自定义的内容。
是否允许 AirPlay 投屏播放,默认允许
哪些媒体文件需要强制用户进行手势交互后才能播放。
该属性将影响 H5
中 video
标签的 autoplay 属性
H5
中的 Video
标签支持局部视频播放,不会全屏视频。Video
标签的 playinline="true"
属性,就可以实现局部播放。A Boolean value indicating whether HTML5 videos may play picture-in-picture.
允许 H5
中 Video
标签支持画中画模式,默认YES
可以使用 H5
中的 JS 代码实现画中画,video.requestPictureInPicture()
,iPhone 不支持,iPad 支持。
用户可以交互选择web视图中的内容的粒度级别.默认是 WKSelectionGranularityDynamic
暂时不知道用于什么场景之下。
支持识别HTML的中字符信息:
UIDataDetectorTypePhoneNumber // 手机号
UIDataDetectorTypeLink // 网页地址
UIDataDetectorTypeAddress // 邮件地址
UIDataDetectorTypeCalendarEvent // 格式化为日历事件的信息
UIDataDetectorTypeShipmentTrackingNumber // 快递包裹信息
UIDataDetectorTypeFlightNumber // 航班号信息
UIDataDetectorTypeLookupSuggestion // 用户可能要查找的信息
UIDataDetectorTypeNone // 默认,不检测
UIDataDetectorTypeAll // 识别全部信息
默认为 UIDataDetectorTypeNone,开启检测会影响网页渲染速度。
ignoresViewportScaleLimits
是否忽略页面缩放限制,默认为 NO。
和H5中的参数存在关联:
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=0.5,user-scalable=yes" />
名称 | ||||
---|---|---|---|---|
ignoresViewportScaleLimits | NO | NO | YES | YES |
user-scalable | yes | no | yes | NO |
结论 | 按照指定尺寸进行缩放 | 无法进行缩放 | 任意放大 | 任意放大 |
- (void)setURLSchemeHandler:(nullable id <WKURLSchemeHandler>)urlSchemeHandler
forURLScheme:(NSString *)urlScheme API_AVAILABLE(macos(10.13), ios(11.0));
- (nullable id <WKURLSchemeHandler>)urlSchemeHandlerForURLScheme:(NSString *)urlScheme API_AVAILABLE(macos(10.13), ios(11.0));
我们可以通过上述方法对 WKWebView 进行自定义协议拦截,无法拦截 http
、https
、ws
、wss
、ftp
协议。
示例代码:
[conf setURLSchemeHandler:[ViewSchemaHandler new] forURLScheme:@"h5"];
在 ViewSchemaHandler 实现协议中的内容
- (void)webView:(WKWebView *)webView startURLSchemeTask:(id<WKURLSchemeTask>)urlSchemeTask
{
// 在这里可以对同一资源进行本地缓存,无需要再次访问。
NSMutableURLRequest *request = urlSchemeTask.request.mutableCopy;
request.URL = [NSURL URLWithString:[request.URL.absoluteString stringByReplacingOccurrencesOfString:@"h5://" withString:@"http://"]];
NSLog(@"%@", request.URL.absoluteURL);
NSURLSession* session = [NSURLSession sharedSession];
NSURLSessionTask* task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
[urlSchemeTask didReceiveResponse:response];
[urlSchemeTask didReceiveData:data];
[urlSchemeTask didFinish];
}];
[task resume];
}
- (void)webView:(WKWebView *)webView stopURLSchemeTask:(id<WKURLSchemeTask>)urlSchemeTask
{
// 当前urlSchemeTask由于某些原因提前结束了(会收到stopURLSchemeTask回调)
}
注意:
H5
前端所有想要拦截的js,css或者其他网络请求资源,需要将路径写成自适应协议路径,例如如下写法即可:<script type="text/javascript" src="//192.168.1.150:3206/7.3.7/js/libs/jquery.min.js" ></script>
H5
前端所有想要拦截的 Ajax 请求,需要将请求更改为相对或者绝对路径,即可实现拦截$.ajax({
url:"/abcd"
});
可以利用上述特性实现应用秒开。
可以通过 decisionHandler
中的 decisionHandler
方法回调。
WKNavigationActionPolicyCancel
取消访问WKNavigationActionPolicyAllow
允许继续访问,默认不实现为该属性示例代码:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
// decisionHandler(WKNavigationActionPolicyCancel);
decisionHandler(WKNavigationActionPolicyAllow);
}
支持偏好设置,暂不理解。
是否允许响应回调,操作同 decidePolicyForNavigationAction
一致。
didStartProvisionalNavigation
发起首次请求会执行这个方法,多次 302 重定向请求,该方法只会执行一次,发生 多次 302 跳转的时候,每次都会先执行 decidePolicyForNavigationAction
,如果这时候用户选择 cancel 操作,则 didReceiveServerRedirectForProvisionalNavigation
方法不会执行。
didReceiveServerRedirectForProvisionalNavigation,发生 302 重定向会走该方法
正常加载地址或者使用js
中的location.href
加载错误的地址发生失败会走该回调。
// oc 代码
[WKWebView loadRequest:] // 发生失败会走该回调
// js代码
location.href="http://abcd" // 发生失败会走该回调
didCommitNavigation
,网络请求加载完成后执行。
didFinishNavigation
完成 js,css,html 渲染后执行。
didFailNavigation
,例如:
window.open("http://abcd") // 发生失败后会走该回调
didReceiveAuthenticationChallenge,可以通过 completionHandler 来选择对证书的操作,例如忽略证书。
/*
NSURLSessionAuthChallengeUseCredential = 0, 使用证书
NSURLSessionAuthChallengePerformDefaultHandling = 1, 忽略证书(默认的处理方式)
NSURLSessionAuthChallengeCancelAuthenticationChallenge = 2, 忽略书证, 并取消这次请求
NSURLSessionAuthChallengeRejectProtectionSpace = 3, 拒绝当前这一次, 下一次再询问
*/
// NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
NSURLCredential *card = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust];
completionHandler(NSURLSessionAuthChallengeUseCredential , card);
webViewWebContentProcessDidTerminate,webView 内容发生崩溃而终止,将会执行该回调方法。
UI代理方法,是H5部分UI操作和原生交互的代理方法,其中包括如下:
H5 中需要打开新窗口的操作,都会被这个方法拦截,例如
<a href="https://www.baidu.com" target="_blank" >打开新窗口</a>
window.open("https://www.baidu.com");
当H5执行 window.close()
方法,则会执行这个代理方法。
当H5执行 window.alert(...args)
方法,则会执行这个代理方法,需要注意:
completionHandler
这个 block
方法必须执行,否则会发生崩溃,弹出窗口如果使用 UIAlertController
作为对接,则要考虑控制器是否存在,是否有并发的弹出窗操作,因为这些会导致 UIAlertController
弹不出来,最终可能在逻辑上造成 completionHandler
无法执行导致崩溃,最好建议弹窗应该使用 UIView 设计。
当H5执行 window.confirm(...args)
,则会执行这个代理方法,注意事项同上。
当H5执行 window.prompt(...args)
,则会执行这个代理方法,注意事项同上。
contextMenu 的相关处理方法,暂时不理解在手机端有何用处。
WKWebView 中 H5 css 动画失效的问题?
目前测试下来
[UIView snapshotViewAfterScreenUpdates:YES];
[UIView drawViewHierarchyInRect:CGRect afterScreenUpdates:YES];
这两种方法在进行系统 UIView
的截图操作时候并且将参数 afterScreenUpdates
设置为 YES 的情况下,最后 频繁调用后会导致H5
中css
动画失效,原因不明。