前言


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

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

  • 更多的支持HTML5的特性

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

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

    https://developer.apple.com/library/mac/documentation/Cocoa/Reference/WebKit/ObjC_classic/index.html

  • Safari相同的JavaScript引擎

  • 占用更少的内存

UIWebView

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 (){

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 (){

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

#import

@protocol WKDelegate

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;

@end

@interface WKDelegateController : UIViewController

@property (weak , nonatomic) id 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: { 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。

  • 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)webViewDelegate;

基本的实现方法和上面写的差不多,就是封装了一下,有兴趣的童鞋可以自己pod下来使用。

IOS 进阶之 WKWebView的更多相关文章

  1. IOS进阶之WKWebView

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

  2. iOS进阶_地图上定位的标志——大头针

    一.添加大头针 地图使用的框架是MapKit 大头针走的是MKAnnotation协议 /* 注意:因为是满足协议MKAnnotation,所以没有MKAnnotation的系统大头针类,必须自定义大 ...

  3. iOS开发之WKWebView简单使用

    iOS开发之WKWebView简单使用   iOS开发之 WKWebVeiw使用 想用UIWebVeiw做的,但是突然想起来在iOS8中出了一个新的WKWebView,算是UIWebVeiw的升级版. ...

  4. iOS进阶指南试读之UI篇

    iOS进阶指南试读之UI篇 UI篇 UI是一个iOS开发工程师的基本功.怎么说?UI本质上就是你调用苹果提供给你的API来完成设计师的设计.所以,想提升UI的功力也很简单,没事就看看UIKit里的各个 ...

  5. iOS进阶读物

    不知不觉作为 iOS 开发也有两年多的时间了,记得当初看到 OC 的语法时,愣是被吓了回去,隔了好久才重新耐下心去啃一啃.啃了一阵,觉得大概有了点概念,看到 Cocoa 那么多的 Class,又懵了, ...

  6. iOS进阶推荐的书目

    <Effective Objective-C 2.0:编写高质量iOS与OS X代码的52个有效方法>([英]Matt Galloway) 很多面试题有涉及 <IOS数据库应用高级编 ...

  7. [iOS开发]WKWebView加载JS

    最近项目要用webView加载js文件,挺同事说WKWebView比UIWebView更加好用,于是我今天就试试,百度一发,自己写了个demo. 先看我写的代码,然后再来看WKWebView跟UIWe ...

  8. iOS进阶之使用 NSURLProtocol 拦截 HTTP 请求(转载)

    这篇文章会提供一种在 Cocoa 层拦截所有 HTTP 请求的方法,其实标题已经说明了拦截 HTTP 请求需要的了解的就是 NSURLProtocol. 由于文章的内容较长,会分成两部分,这篇文章介绍 ...

  9. ios开发之--WKWebView的使用

    WKWebView是ios 8 出来的,是为了解决UIWebView卡慢,占用内存过大的问题. 在以往时候,如果用UIWebView加载加载网页的时候,卡慢现象会很严重,有时候往往会卡到一个页面无法动 ...

随机推荐

  1. 简明python教程 --C++程序员的视角(六):输入输出IO

    程序与用户交互 你会从用户那里得到输入,然后打印一些结果.我们可以分别使用raw_input,input和print语句来完成这些功能.raw_input会返回字符串,而input会返回字面值,相当于 ...

  2. 6 cocos2dx粒子效果,类图关系,系统原生粒子和自己定义粒子效果,粒子编译器软件,爆炸粒子效果,烟花效果,火焰效果,流星效果,漩涡粒子效果,雪花效果,烟雾效果,太阳效果,下雨效果

     1 粒子 演示样例 2 类图关系 3 系统原生粒子 CCParticleSystem 全部粒子系统的父类 CCParticleSystemPoint. CCParticleSystemQuad ...

  3. @Logback简介

    Ceki Gülcü在Java日志领域世界知名.他创造了Log4J,这个最早的Java日志框架即便在JRE内置日志功能的竞争下仍然非常流行.随后他又着手实现SLF4J这个"简单的日志前端接口 ...

  4. Linked List Cycle leetcode II java (寻找链表环的入口)

    题目: Given a linked list, return the node where the cycle begins. If there is no cycle, return null. ...

  5. 科幻大片中那些牛X代码真相

    在<黑客帝国>中,救世主Neo的队友通过屏幕上"1"和"0"构成的数据流,就能看到鲜活的画面,这应该算是科幻大片中对代码最极致的表现了.其他科幻电影 ...

  6. 【MySQL】MySQL统计NULL字段处理

    select A.user_id,ifnull(sum(ifnull(A.ex_cpu,0)),0) AS a0, ifnull(sum(ifnull(A.ex_mem,0)),0) AS a1, i ...

  7. Linq-分页查询

    var list = from s in db.t_address select s; & userid != null) { list = list.Where(v => v.user ...

  8. [转] ssh免密码登录服务器

    1. tencent 同事写的比较靠谱 https://www.chenyudong.com/archives/ssh-using-private-public-key-no-password.htm ...

  9. GBDT(Gradient Boost Decision Tree)

    原文:http://blog.csdn.net/aspirinvagrant/article/details/48415435 GBDT,全称Gradient Boosting Decision Tr ...

  10. dev -c++ 快捷键

    转自:http://blog.csdn.net/abcjennifer/article/details/7259222 F8:开始调试 F7:进一步执行当前行,并跳到下一行 F4:添加查看 ctrl ...