WKWebView是iOS8.0以后推出的新框架WebKit
中用于替代UIWebView的新组件, 它在加载速度和内存使用上面都比UIWebView强太多.
WKWebView
加载网络或者本地HTML
1 | - (nullable WKNavigation *)loadRequest:(NSURLRequest *)request; |
加载本地JavaScript代码
WKNavigationDelegate协议
加载过程代理方法
1 | // 页面开始加载时调用 |
跳转代理方法
1 | // 接收到服务器跳转请求之后调用 |
WKUIDelegate协议
这个协议主要用于页面上面的提示框的处理以及允许不安全的链接导航.1
2
3
4
5
6
7
8
9
10
11//在JS端调用alert函数时,会触发此代理方法。
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;
//JS端调用confirm函数时,会触发此方法
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
//JS端调用prompt函数时,会触发此方法
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler;
//处理不安全的链接允许导航, 默认不处理不允许导航
- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;
WKWebView加载进度和标题
WKWebView提供了一个属性estimatedProgress可以通过KVO实时显示网页加载进度, 而UIWebView是没有的, 以前的通用做法都是做一个虚假的进度条.还有一个属性title, 网页的标题也可以获取了.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16@property (nullable, nonatomic, readonly, copy) NSString *title;
@property (nonatomic, readonly) double estimatedProgress;
//最后注意需要在dealloc中移除观察者
[self.wkWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
//KVO方法
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
if (object == self.wkWebView && [keyPath isEqualToString:@"estimatedProgress"] ) {
CGFloat newProgress = [[change objectForKey:NSKeyValueChangeNewKey] floatValue];
NSLog(@"%f",newProgress);
}
}
与iOS的交互
iOS调用JavaScript中方法
WKWebView提供了下面的方法, 弥补了UIWebView类似方法返回值类型只能为NSString以及无法捕捉错误的缺陷.1
2
3
4
5
6- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id result, NSError * _Nullable error))completionHandler;
//例如除了可以通过self.webView.title获取标题, 下面方法也可以
[self.webView evaluateJavaScript:@"document.title" completionHandler:^(id _Nullable title, NSError * _Nullable error) {
//code
}];
JavaScript调用iOS中方法
URL拦截
我们依然可以通过自定义Scheme拦截URL, 根据URL调用相应的方法. 在WKWebView中需要在下面的代理方法中拦截. 例如在<a href="#toLogin">登录</a>
中1
2//WKWebView
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
通过WKUserContentController实现
//注册一个webViewHandles方法 (这样会造成内存泄露)
[wkWebView.configuration.userContentController addScriptMessageHandler:self name:@”webViewHandles”];
需要代理遵循WKScriptMessageHandler并实现下面方法:1
2
3- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
//message.body,message.frameInfo
}
在h5端通过下面方式调用 body是传递的参数. window.webkit.messageHandlers.webViewHandles.postMessage({body: 'hello world!'});
正确的做法: 新建一个代理类来处理
.h文件中1
2
3
4
5
6
7
8
9
10
11
12
13#import <Foundation/Foundation.h>
#import <WebKit/WKScriptMessageHandler.h>
@interface HJWeakScriptMessageDelegate : NSObject<WKScriptMessageHandler>
@property (nonatomic, weak) id<WKScriptMessageHandler> scriptDelegate;
- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate;
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithFrame:(CGRect)frame NS_UNAVAILABLE;
- (instancetype)initWithCoder:(NSCoder *)aDecoder NS_UNAVAILABLE;
@end
.m中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#import "HJWeakScriptMessageDelegate.h"
@implementation HJWeakScriptMessageDelegate
- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate {
self = [super init];
if (self) {
_scriptDelegate = scriptDelegate;
}
return self;
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if (self.scriptDelegate && [self.scriptDelegate respondsToSelector:@selector(userContentController:didReceiveScriptMessage:)]) {
[self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
}
}
@end
修改上面代码为:
[WKWebView.configuration.userContentController addScriptMessageHandler:[[HJWeakScriptMessageDelegate alloc] initWithDelegate:self] name:@”webViewHandles”];