IOS进阶之WKWebView

简介:

前言


Xcode8发布以后,编译器开始不支持IOS7,所以很多应用在适配IOS10之后都不在适配IOS7了,其中包括了很多大公司,网易新闻,滴滴出行等。因此,我们公司的应用也打算淘汰IOS7。

支持到IOS8,第一个要改的自然是用WKWebView替换原来的UIWebView。WKWebView有很多明显优势:

  • 更多的支持HTML5的特性

  • 官方宣称的高达60fps的滚动刷新率以及内置手势

  • 将UIWebViewDelegate与UIWebView拆分成了14类与3个协议,以前很多不方便实现的功能得以实现。文档

  • Safari相同的JavaScript引擎

  • 占用更少的内存

UIWebView


UIWebView

WKWebView


WKWebView

因此,使用WkWebview替换UIWebView还是很有必要的。

基本使用方法


WKWebView有两个delegate,WKUIDelegate 和 WKNavigationDelegate。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/ }>

注意点

  • addScriptMessageHandler要和removeScriptMessageHandlerForName配套出现,否则会造成内存泄漏。

  • h5只能传一个参数,如果需要多个参数就需要用字典或者json组装。


oc调用JS方法

代码如下:

- (void)webView:(WKWebView *)tmpWebView didFinishNavigation:(WKNavigation *)navigation{    //say()是JS方法名,completionHandler是异步回调block
    [webView evaluateJavaScript:@"say()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {        NSLog(@"%@",result);
    }];

}

h5代码同上。

WebViewJavascriptBridge


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

主要方法如下:

//初始化方法
+ (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下来使用。



文/o翻滚的牛宝宝o(简书作者)
原文链接:http://www.jianshu.com/p/4fa8c4eb1316
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

我虐代码千百遍,代码待我如初恋!
















本文转自ljianbing51CTO博客,原文链接: http://blog.51cto.com/ljianbing/1871797,如需转载请自行联系原作者


相关文章
|
Android开发 iOS开发
iOS 逆向编程(四)实操 Jailbreak 进阶必备软件
iOS 逆向编程(四)实操 Jailbreak 进阶必备软件
99 0
|
缓存 JavaScript iOS开发
iOS 逆向编程(十五)Cycript 语法进阶(封装 .cy 脚本文件)
iOS 逆向编程(十五)Cycript 语法进阶(封装 .cy 脚本文件)
185 0
|
Android开发 iOS开发
iOS 逆向编程(四)实操越狱进阶必备软件
iOS 逆向编程(四)实操越狱进阶必备软件
106 0
|
iOS开发
iOS WKWebView 打开页面空白URL为空问题解决办法
iOS WKWebView 打开页面空白URL为空问题解决办法
665 0
|
移动开发 iOS开发
iOS WKWebView h5使用alert方法不起作用解决方法
iOS WKWebView h5使用alert方法不起作用解决方法
550 0
|
存储 前端开发 测试技术
iOS 模块化进阶整理记录(下)
iOS 模块化进阶整理记录(下)
179 0
iOS 模块化进阶整理记录(下)
|
Android开发 iOS开发 开发者
iOS 模块化进阶整理记录(中)
iOS 模块化进阶整理记录(中)
299 0
iOS 模块化进阶整理记录(中)
|
存储 前端开发 中间件
iOS 模块化进阶整理记录(上)
iOS 模块化进阶整理记录(上)
455 0
iOS 模块化进阶整理记录(上)
|
缓存 安全 程序员
iOS多线程进阶(下)
iOS多线程进阶(下)
200 0
iOS多线程进阶(下)
|
调度 数据安全/隐私保护 iOS开发
iOS多线程进阶(上)
iOS多线程进阶
187 0
iOS多线程进阶(上)