由于我最近比较忙,先把代码贴出来,以后再增加说明。
本文以加载本地h5页面方式进行演示,若是想把它变成加载网址的方式。只需要把[self loadWebHTMLSring:@“AWWKTest”];换成[self loadWebHTMLSring:url];就可以,当然url为h5的地址;也可以在跳转页面直接调用loadWebHTMLSring方法记载地址。
测试的本地h5文件AWWKTest.html代码,就是把这个文件加入工程中就可以。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf8">
<script language="javascript">
//JS执行window.webkit.messageHandlers.onMenuShareTimeline.postMessage(<messageBody>)
function onMenuShareTimelineClick() {
window.webkit.messageHandlers.share.postMessage({title:'测试分享的标题',content:'测试分享的内容',url:'https://github.com/mayin1992'});
}
//分享回调结果显示
function shareResult(channel_id,share_channel,share_url){
var content = channel_id+","+share_channel+","+share_url;
alert(content);
document.getElementById("returnValue").value = content;
}
</script>
</head>
<body>
<h1>这是按钮调用</h1>
<input type="btton" value="分享" onclick="onMenuShareTimelineClick()" />
<h1>回调展示区</h1>
<textarea id ="returnValue" type="value" rows="5" cols="40">
</textarea>
</body>
</html>
AWWKWebViewController.h文件代码:
#import <UIKit/UIKit.h>
@interface AWWKWebViewController : UIViewController
/** 是否显示Nav */
@property (nonatomic,assign) BOOL isNavHidden;
@property(nonatomic,assign) BOOL isHaveTelLoginPage;
//@property (nonatomic, copy) void(^removeTelLoginPageCallBack)(void);
/**
加载纯外部链接网页
@param string URL地址
*/
- (void)loadWebURLSring:(NSString *)string;
/**
加载本地网页
@param string 本地HTML文件名
*/
- (void)loadWebHTMLSring:(NSString *)string;
/**
加载外部链接POST请求(注意检查 XFWKJSPOST.html 文件是否存在 )
postData请求块 注意格式:@"\"username\":\"xxxx\",\"password\":\"xxxx\""
@param string 需要POST的URL地址
@param postData post请求块
*/
- (void)POSTWebURLSring:(NSString *)string postData:(NSString *)postData;
@end
AWWKWebViewController.m文件代码。这个是js调用oc的方法,oc直接调用js的方法并传参数,也可以oc处理了一些事情后或用户点击了oc的按钮后调用js的方法,自己根据自己的需要可以自己改造。
#import "AWWKWebViewController.h"
#import <WebKit/WKWebView.h>
#import <WebKit/WebKit.h>
#import "AWJsWebEntity.h"
typedef enum{
loadWebURLString = 0,
loadWebHTMLString,
POSTWebURLString,
}wkWebLoadType;
static void *WkwebBrowserContext = &WkwebBrowserContext;
@interface AWWKWebViewController ()<WKNavigationDelegate,WKUIDelegate,WKScriptMessageHandler,UINavigationControllerDelegate,UINavigationBarDelegate>
@property (nonatomic, strong) WKWebView *wkWebView;
//设置加载进度条
@property (nonatomic,strong) UIProgressView *progressView;
//仅当第一次的时候加载本地JS
@property(nonatomic,assign) BOOL needLoadJSPOST;
//网页加载的类型
@property(nonatomic,assign) wkWebLoadType loadType;
//保存的网址链接
@property (nonatomic, copy) NSString *URLString;
//保存POST请求体
@property (nonatomic, copy) NSString *postData;;
@property (nonatomic,strong) UIButton *rightButton;
@property (nonatomic,strong) UIButton *leftBtn;
@property (nonatomic,strong) NSMutableString *cookieValue;
@end
@implementation AWWKWebViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self loadWebHTMLSring:@"AWWKTest"];
//加载web页面
[self webViewloadURLType];
//添加到主控制器上
[self.view addSubview:self.wkWebView];
//添加进度条
[self.view addSubview:self.progressView];
UIBarButtonItem *barButton = [[UIBarButtonItem alloc]initWithCustomView:self.rightButton];
self.navigationItem.rightBarButtonItem = barButton;
UIBarButtonItem *leftBarButton = [[UIBarButtonItem alloc]initWithCustomView:self.leftBtn];
self.navigationItem.leftBarButtonItem = leftBarButton;
@weakify(self);
[RACObserve(self.wkWebView.scrollView, contentSize) subscribeNext:^(id x) {
@strongify(self);
NSLog(@"xxxxxx:%@",x);
[self updateNavigationItems];
}];
}
-(UIButton*)leftBtn
{
if (!_leftBtn)
{
_leftBtn =[[UIButton alloc]initWithFrame:CGRectMake(10, 20, 44, 44)];
[_leftBtn setImage:[UIImage imageNamed:@"back"] forState:UIControlStateNormal];
[_leftBtn addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
_leftBtn.hidden=NO;
}
return _leftBtn;
}
-(void)back
{
[self goToLastView];
}
-(void)goToLastView
{
if ([self.wkWebView canGoBack])
{
[self.wkWebView goBack];
}
else
{
WKBackForwardListItem *currentItem = self.wkWebView.backForwardList.currentItem;
NSString *url = [currentItem.URL.absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if([url rangeOfString:telLoginHtml].location != NSNotFound)
{
[self.navigationController popViewControllerAnimated:YES];
}
else
{
[[AWNoticeView currentNotice] showErrorNotice:@"你已经在主页,不能后退了"];
}
// [self.navigationController popViewControllerAnimated:YES];
}
}
-(UIButton*)rightButton
{
if (!_rightButton)
{
_rightButton =[[UIButton alloc]initWithFrame:CGRectMake(FULL_WIDTH - 10 - 44, 20, 44, 44)];
[_rightButton setImage:[UIImage imageNamed:@"share"] forState:UIControlStateNormal];
[_rightButton addTarget:self action:@selector(roadLoadClicked) forControlEvents:UIControlEventTouchUpInside];
_rightButton.hidden=NO;
}
return _rightButton;
}
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
// if (self.removeTelLoginPageCallBack)
// {
// self.removeTelLoginPageCallBack();
// }
if (_isNavHidden == YES) {
self.navigationController.navigationBarHidden = YES;
//创建一个高20的假状态栏
UIView *statusBarView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 20)];
//设置成绿色
statusBarView.backgroundColor=[UIColor whiteColor];
// 添加到 navigationBar 上
[self.view addSubview:statusBarView];
}else{
self.navigationController.navigationBarHidden = NO;
}
}
- (void)roadLoadClicked{
[self.wkWebView reload];
}
-(void)customBackItemClicked{
if (self.wkWebView.goBack) {
[self.wkWebView goBack];
}else{
[self.navigationController popViewControllerAnimated:YES];
}
}
-(void)closeItemClicked{
[self.navigationController popViewControllerAnimated:YES];
}
#pragma mark ================ 加载方式 ================
- (void)webViewloadURLType{
switch (self.loadType) {
case loadWebURLString:{
//创建一个NSURLRequest 的对象
// NSURLRequest * Request_zsj = [NSURLRequest requestWithURL:[NSURL URLWithString:self.URLString] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
// //加载网页
// [self.wkWebView loadRequest:Request_zsj];
NSMutableURLRequest * Request_zsj = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:self.URLString] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
// if(self.cookieValue)
// {
// [Request_zsj addValue:self.cookieValue forHTTPHeaderField:@"Cookie"];
// }
//加载网页
[self.wkWebView loadRequest:Request_zsj];
break;
}
case loadWebHTMLString:{
[self loadHostPathURL:self.URLString];
break;
}
case POSTWebURLString:{
// JS发送POST的Flag,为真的时候会调用JS的POST方法
self.needLoadJSPOST = YES;
//POST使用预先加载本地JS方法的html实现,请确认WKJSPOST存在
[self loadHostPathURL:@"AWWKJSPOST.htm"];
break;
}
}
}
- (void)loadHostPathURL:(NSString *)url{
//获取JS所在的路径
NSString *path = [[NSBundle mainBundle] pathForResource:url ofType:@"html"];
//获得html内容
NSString *html = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
//加载js
[self.wkWebView loadHTMLString:html baseURL:[[NSBundle mainBundle] bundleURL]];
}
// 调用JS发送POST请求
- (void)postRequestWithJS {
// 拼装成调用JavaScript的字符串
NSString *jscript = [NSString stringWithFormat:@"post('%@',{%@});", self.URLString, self.postData];
// 调用JS代码
[self.wkWebView evaluateJavaScript:jscript completionHandler:^(id object, NSError * _Nullable error) {
}];
}
- (void)loadWebURLSring:(NSString *)string{
self.URLString = string;
self.loadType = loadWebURLString;
}
- (void)loadWebHTMLSring:(NSString *)string{
self.URLString = string;
self.loadType = loadWebHTMLString;
}
- (void)POSTWebURLSring:(NSString *)string postData:(NSString *)postData{
self.URLString = string;
self.postData = postData;
self.loadType = POSTWebURLString;
}
#pragma mark ================ 自定义返回/关闭按钮 ================
-(void)updateNavigationItems{
WKBackForwardListItem *currentItem = self.wkWebView.backForwardList.currentItem;
NSString *url = [currentItem.URL.absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(@"current url:%@", url);
self.navigationItem.leftBarButtonItem.customView.hidden = [AWGeneralFunction isHiddenWebBackButton:url];
}
#pragma mark ================ WKNavigationDelegate ================
//这个是网页加载完成,导航的变化
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
/*
主意:这个方法是当网页的内容全部显示(网页内的所有图片必须都正常显示)的时候调用(不是出现的时候就调用),,否则不显示,或则部分显示时这个方法就不调用。
*/
// 判断是否需要加载(仅在第一次加载)
if (self.needLoadJSPOST) {
// 调用使用JS发送POST请求的方法
[self postRequestWithJS];
// 将Flag置为NO(后面就不需要加载了)
self.needLoadJSPOST = NO;
}
// 获取加载网页的标题
self.title = self.wkWebView.title;
// [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
[self updateNavigationItems];
// NSArray*backList = self.wkWebView.backForwardList.backList;
// NSLog(@"backList:%@, backList.count = %lld", backList, backList.count);
}
//开始加载
-(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
//开始加载的时候,让加载进度条显示
self.progressView.hidden = NO;
}
//内容返回时调用
-(void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{}
//服务器请求跳转的时候调用
-(void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{}
//服务器开始请求的时候调用
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSString *strRequest = [navigationAction.request.URL.absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(@"isHaveTelLoginPage:%d, strRequest:%@",[AWSingleObject sharedInstance].isHaveTelLoginPage, strRequest);
if(isEmptyString(strRequest))
{
decisionHandler(WKNavigationActionPolicyCancel);//不允许跳转
return;
}
else if([strRequest isEqualToString:@"about:blank"]) {//主页面加载内容
decisionHandler(WKNavigationActionPolicyAllow);//允许跳转
}
switch (navigationAction.navigationType) {
case WKNavigationTypeLinkActivated: {
break;
}
case WKNavigationTypeFormSubmitted: {
break;
}
case WKNavigationTypeBackForward: {
break;
}
case WKNavigationTypeReload: {
break;
}
case WKNavigationTypeFormResubmitted: {
break;
}
case WKNavigationTypeOther: {
break;
}
default: {
break;
}
}
[self updateNavigationItems];
decisionHandler(WKNavigationActionPolicyAllow);
}
// 内容加载失败时候调用
-(void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error{
NSLog(@"页面加载超时");
}
//跳转失败的时候调用
-(void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{}
//进度条
-(void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{}
#pragma mark ================ WKUIDelegate ================
// 获取js 里面的提示
-(void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}]];
[self presentViewController:alert animated:YES completion:NULL];
}
//KVO监听进度条
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(estimatedProgress))] && object == self.wkWebView) {
[self.progressView setAlpha:1.0f];
BOOL animated = self.wkWebView.estimatedProgress > self.progressView.progress;
[self.progressView setProgress:self.wkWebView.estimatedProgress animated:animated];
// Once complete, fade out UIProgressView
if(self.wkWebView.estimatedProgress >= 1.0f) {
[UIView animateWithDuration:0.3f delay:0.3f options:UIViewAnimationOptionCurveEaseOut animations:^{
[self.progressView setAlpha:0.0f];
} completion:^(BOOL finished) {
[self.progressView setProgress:0.0f animated:NO];
}];
}
}
else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#pragma mark ================ WKScriptMessageHandler ================
//拦截执行网页中的JS方法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
//服务器固定格式写法 window.webkit.messageHandlers.名字.postMessage(内容);
//客户端写法 message.name isEqualToString:@"名字"]
if ([message.name isEqualToString:@"share"]) {
NSLog(@"%@", message.body);
//oc调用js的shareResult方法并且传递参数
[self.wkWebView evaluateJavaScript:@"shareResult('dd', 'aa', '56');" completionHandler:^(id _Nullable data, NSError * _Nullable error) {
}];
}
}
#pragma mark ================ 懒加载 ================
- (WKWebView *)wkWebView{
if (!_wkWebView) {
//设置网页的配置文件
WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc]init];
//允许视频播放
if (@available(iOS 9.0, *)) {
configuration.allowsAirPlayForMediaPlayback = YES;
} else {
// Fallback on earlier versions
}
// 允许在线播放
configuration.allowsInlineMediaPlayback = YES;
// 允许可以与网页交互,选择视图
configuration.selectionGranularity = YES;
// web内容处理池
configuration.processPool = [[WKProcessPool alloc] init];
//自定义配置,一般用于 js调用oc方法(OC拦截URL中的数据做自定义操作)
WKUserContentController * userContentController = [[WKUserContentController alloc]init];
// 添加消息处理,注意:self指代的对象需要遵守WKScriptMessageHandler协议,结束时需要移除
[userContentController addScriptMessageHandler:self name:@"share"];
WKPreferences *preferences = [WKPreferences new];
preferences.javaScriptCanOpenWindowsAutomatically = YES;
configuration.preferences = preferences;
// 是否支持记忆读取
configuration.suppressesIncrementalRendering = YES;
// 允许用户更改网页的设置
configuration.userContentController = userContentController;
_wkWebView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
_wkWebView.backgroundColor = [UIColor whiteColor];//[UIColor colorWithRed:240.0/255 green:240.0/255 blue:240.0/255 alpha:1.0];
// 设置代理
_wkWebView.navigationDelegate = self;
_wkWebView.UIDelegate = self;
//kvo 添加进度监控
[_wkWebView addObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress)) options:0 context:WkwebBrowserContext];
//开启手势触摸
_wkWebView.allowsBackForwardNavigationGestures = YES;
// 设置 可以前进 和 后退
//适应你设定的尺寸
[_wkWebView sizeToFit];
}
return _wkWebView;
}
- (UIProgressView *)progressView{
if (!_progressView) {
_progressView = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleDefault];
if (_isNavHidden == YES) {
_progressView.frame = CGRectMake(0, 20, self.view.bounds.size.width, 3);
}else{
_progressView.frame = CGRectMake(0, 64, self.view.bounds.size.width, 3);
}
// 设置进度条的色彩
[_progressView setTrackTintColor:[UIColor colorWithRed:240.0/255 green:240.0/255 blue:240.0/255 alpha:1.0]];
_progressView.progressTintColor = [UIColor colorWithHexString:@"0X057dff"];//[UIColor greenColor];
}
return _progressView;
}
-(void)viewWillDisappear:(BOOL)animated{
[self.wkWebView.configuration.userContentController removeScriptMessageHandlerForName:@"share"];
[self.wkWebView setNavigationDelegate:nil];
[self.wkWebView setUIDelegate:nil];
}
//注意,观察的移除
-(void)dealloc{
[self.wkWebView removeObserver:self forKeyPath:NSStringFromSelector(@selector(estimatedProgress))];
}
@end
iOS调用js方法:
-(void)sendJsSuccessMessageWithParams:(NSMutableDictionary *)params
{
if(isCommonUnitEmptyDict(params))
{
return;
}
NSString *callBackStr = [NSString stringWithFormat:@"success(%@);", [params getJsonString]];
[self callBackWithCallBackStr:callBackStr];//注意:函数里面这个要是去除换行符的json字符串
}
-(void)sendJsRechargeFailedMessage
{
NSString *callBackStr = [NSString stringWithFormat:@"failed();"];
[self callBackWithCallBackStr:callBackStr];
}
-(void)callBackWithCallBackStr:(NSString *)callBackStr
{
NSLog(@"callBackStr :%@", callBackStr);
if(isCommonUnitEmptyString(callBackStr))
{
return;
}
bg_dispatch_main_async_safe(^{
//oc调用js的shareResult方法并且传递参数
[self.wkWebView evaluateJavaScript:callBackStr completionHandler:^(id _Nullable data, NSError * _Nullable error) {
}];
});
}
这个是具体调用例子:
NSMutableDictionary *params = [NSMutableDictionary dictionary];
[params setValue:@"0.01" forKey:@"amount"];
[params setValue:@"cardNameValue" forKey:@"cardName"];
[self sendJsSuccessMessageWithParams:params];
由于最近项目正做根据微信端直接产生原生app,并保存微信app所有页面。踩了很多坑,特别是WKWebView的坑不是一般的深。我有其它文章解决:WKWebView和js互调方法参数传递及注意事项见文章《向js发送含有NSDictionary对象或NSArray对象的消息》。完美解决通过TZImagePickerController组件获取的多选图片对象及PHAsset数组,通过PHAsset直接获取到的图像被旋转问题及图片名称的获取见文章《通过PHAsset获取的图片上传后变大和图像被旋转90度问题完美解决方案》。WKWebView对图片地址的拦截及获取本地图片并显示问题见文章《WKWebView采用HybridNSURLProtocol协议拦截图片等资源预加载》。