IOS进阶之WKWebView

96
远0
2016.12.30 14:47* 字数 426

基本使用方法
WKWebView有两个delegate,WKUIDelegateWKNavigationDelegate。WKNavigationDelegate主要处理一些跳转、加载处理操作,WKUIDelegate主要处理JS脚本,确认框,警告框等。因此WKNavigationDelegate更加常用。
比较常用的方法:

#pragma mark - lifeCircle
- (void)viewDidLoad { 
[super viewDidLoad]; 
webView = [[WKWebView alloc]init]; 
[self.view addSubview:webView]; 
[webView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.view); make.right.equalTo(self.view); make.top.equalTo(self.view); make.bottom.equalTo(self.view); 
}]; 
webView.UIDelegate = self; 
webView.navigationDelegate = self; 
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];
}
#pragma mark- WKNavigationDelegate
// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
}
// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{
}
// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
}
// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{
}
// 接收到服务器跳转请求之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{
}
// 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
 NSLog(@"%@",navigationResponse.response.URL.absoluteString);
 //允许跳转 
decisionHandler(WKNavigationResponsePolicyAllow); 
//不允许跳转 
//decisionHandler(WKNavigationResponsePolicyCancel);
}
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{ NSLog(@"%@",navigationAction.request.URL.absoluteString); 
//允许跳转 
decisionHandler(WKNavigationActionPolicyAllow); 
//不允许跳转 //decisionHandler(WKNavigationActionPolicyCancel);
}
#pragma mark - WKUIDelegate
// 创建一个新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{ 
  return [[WKWebView alloc]init];
}
// 输入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler{ 
  completionHandler(@"http");
}
// 确认框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler{ 
  completionHandler(YES);
}
// 警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{ 
NSLog(@"%@",message); completionHandler();
}

OC与JS交互
WKWebview提供了API实现js交互 不需要借助JavaScriptCore或者webJavaScriptBridge。使用WKUserContentController实现js native交互。简单的说就是先注册约定好的方法,然后再调用。

JS调用OC方法
oc代码(有误,内存不释放):

@interface ViewController ()<WKUIDelegate,WKNavigationDelegate,WKScriptMessageHandler>
{ 
WKWebView * webView;
 WKUserContentController* userContentController;
}
@end
@implementation ViewController
#pragma mark - lifeCircle
- (void)viewDidLoad {
 [super viewDidLoad]; 
//配置环境 
WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc]init];
userContentController =[[WKUserContentController alloc]init]; 
configuration.userContentController = userContentController;
webView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, 100, 100) configuration:configuration]; 
//注册方法 
[userContentController addScriptMessageHandler:self name:@"sayhello"];
//注册一个name为sayhello的js方法
 [self.view addSubview:webView]; 
[webView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.view); make.right.equalTo(self.view); make.top.equalTo(self.view); make.bottom.equalTo(self.view); 
}]; 
webView.UIDelegate = self; 
webView.navigationDelegate = self; 
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.test.com"]]];
}
- (void)dealloc{ 
//这里需要注意,前面增加过的方法一定要remove掉。 [userContentController removeScriptMessageHandlerForName:@"sayhello"];}
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ 
NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);
}
@end

上面的OC代码如果认证测试一下就会发现dealloc并不会执行,这样肯定是不行的,会造成内存泄漏。原因是[userContentController addScriptMessageHandler:self name:@"sayhello"];这句代码造成无法释放内存。(ps:试了下用weak指针还是不能释放,不知道是什么原因。)因此还需要进一步改进,正确的写法是用一个新的controller来处理,新的controller再绕用delegate绕回来。

oc代码(正确写法):

@interface ViewController ()<WKUIDelegate,WKNavigationDelegate,WKScriptMessageHandler>
{ 
WKWebView * webView; 
WKUserContentController* userContentController;
}
@end
@implementation ViewController
#pragma mark - lifeCircle
- (void)viewDidLoad { 
[super viewDidLoad]; 
//配置环境 
WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc]init]; 
userContentController =[[WKUserContentController alloc]init]; configuration.userContentController = userContentController; 
webView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, 100, 100) configuration:configuration]; 
//注册方法 WKDelegateController * delegateController = [[WKDelegateController alloc]init]; 
delegateController.delegate = self; 
[userContentController addScriptMessageHandler:delegateController name:@"sayhello"]; 
[self.view addSubview:webView]; 
[webView mas_makeConstraints:^(MASConstraintMaker *make) { make.left.equalTo(self.view); make.right.equalTo(self.view); make.top.equalTo(self.view); make.bottom.equalTo(self.view); 
}]; 
webView.UIDelegate = self; 
webView.navigationDelegate = self;
 [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.test.com"]]];
}
- (void)dealloc{ 
//这里需要注意,前面增加过的方法一定要remove掉。 [userContentController removeScriptMessageHandlerForName:@"sayhello"];
}
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ 
NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);
}
@end

WKDelegateController代码:

#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
@protocol WKDelegate <NSObject>
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
@end
@interface WKDelegateController : UIViewController <WKScriptMessageHandler>
@property (weak , nonatomic) id<WKDelegate> delegate;
@end

.m代码:

#import "WKDelegateController.h"
@interface WKDelegateController ()
@end
@implementation WKDelegateController
- (void)viewDidLoad {
 [super viewDidLoad];
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ 
if ([self.delegate respondsToSelector:@selector(userContentController:didReceiveScriptMessage:)]) { 
  [self.delegate userContentController:userContentController didReceiveScriptMessage:message];
 }
}
@end

h5代码:

<html><head> <script>function say(){//前端需要用 window.webkit.messageHandlers.注册的方法名.postMessage({body:传输的数据} 来给native发送消息 window.webkit.messageHandlers.sayhello.postMessage({body: 'hello world!'});}</script></head> <body> <h1>hello world</h1> <button onclick="say()">say hello</button> </body></html>

打印出的log:

 name:sayhello
 body:{ 
  body = "hello world!";
} 
frameInfo:<WKFrameInfo: 0x7f872060ce20; isMainFrame = YES;
 request = <NSMutableURLRequest: 0x618000010a30> { URL: http://www.test.com/ }>

WebViewJavascriptBridge
一般来说,一个好的UI总有一个大神会开发出一个好的第三方封装框架。WebViewJavascriptBridge的作者也做了一套支持WKWebView与JS交互的第三方框架:WKWebViewJavascriptBridge。
cocoaPods: pod 'WebViewJavascriptBridge', '~> 5.0.5'

github地址:https://github.com/marcuswestin/WebViewJavascriptBridge

主要方法如下:

//初始化方法
+ (instancetype)bridgeForWebView:(WKWebView*)webView;
+ (void)enableLogging;
//注册函数名
- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler;
//调用函数名
- (void)callHandler:(NSString*)handlerName;
- (void)callHandler:(NSString*)handlerName data:(id)data;
- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback;
//重置
- (void)reset;
//设置WKNavigationDelegate
- (void)setWebViewDelegate:(id<WKNavigationDelegate>)webViewDelegate;

基本的实现方法和上面写的差不多,就是封装了一下,有兴趣的童鞋可以自己pod下来使用。
转载请保留原文链接!!!

日记本
Gupao