iOS之JavaScript与OC的相互调用:WKwebview 的使用

2024-05-31 10:08

本文主要是介绍iOS之JavaScript与OC的相互调用:WKwebview 的使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

#import <WebKit/WebKit.h>
添加代理<WKUIDelegate,WKNavigationDelegate,WKScriptMessageHandler>//创建一个WKWebView的配置对象
WKWebViewConfiguration *configur=[[WKWebViewConfiguration alloc]init];
//设置configur对象的preferences属性的信息
WKPreferences *preferences = [[WKPreferences alloc]init];
configur.preferences=preferences;
//是否允许与js进行交互,默认是YES的,如果设置为NO,js的代码就不起作用了
preferences.javaScriptEnabled =YES;
WKUserContentController *userContentController=[[WKUserContentController alloc]init];
//添加消息处理,注意:self指代的是需要遵守WKScriptMessageHandler协议,结束时需要移除
[userContentController addScriptMessageHandler:self name:@"showSendMsg"];
configur.userContentController=userContentController;//解决跨域问题[configur.preferences setValue:@YES forKey:@"allowFileAccessFromFileURLs"];if (@available(iOS 10.0, *)) {[configur setValue:@YES forKey:@"allowUniversalAccessFromFileURLs"];}
WKWebView *wkWebView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0,WIDTH , HEIGHT) configuration:configur];
[self.view addSubview:wkWebView];
//设置内边距底部,主要是为了让网页最后的内容不被底部的toolBar挡着
//wkWebView.scrollView.contentInset=UIEdgeInsetsMake(0, 0, 104, 0);
//这句代码是让竖直方向的滚动条显示在正确的位置
wkWebView.scrollView.scrollIndicatorInsets=wkWebView.scrollView.contentInset;wkWebView.UIDelegate=self;
wkWebView.navigationDelegate=self;//加载html字符串
//[wkWebView loadHTMLString:html StrbaseURL:nil];//加载html网页
NSURL *url = [[NSBundle mainBundle] URLForResource:@"suning"withExtension:@"html"];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];[wkWebView loadRequest:urlRequest];从web界面中接收到一个jsscript脚本时调用
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
//如果传递的参数是json的话//id param =message.body;//  NSString *urlString=@"";//if([param isKindOfClass:[NSDictionary class]]){//   if([[param allKeys]containsObject:@"func"]){//  urlString=[param valueForKey:@"func"];// }// }NSString *coverWebview=message.name;
NSLog(@"js中的方法 ---%@--传递的参数-%@",coverWebview,message.body);
//如果方法名是我们需要的,那么说明是时候调用原生对应的方法了
if([coverWebview isEqualToString:@"showSendMsg"]){[self showSendMsg];
}}-(void)showSendMsg{
NSLog(@"js调用OC方法实现了");}html代码:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0"><title>苏宁首页,rem+less布局+媒体查询</title>
</head>
<body><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><h1>dafdfcdsadsad</h1><script type='text/javascript'>function btnClick3() {window.webkit.messageHandlers.showSendMsg.postMessage(['123', 'qqqq'])}btnClick3();</script>
</body>
</html>

js调用oc方法二:采用截获url

//ios13
//WKWebView拦截url进行原生界面跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {// 获取完整url并进行UTF-8转码NSString *strRequest = [navigationAction.request.URL.absoluteString stringByRemovingPercentEncoding];NSLog(@"即将跳转到的那个url---%@",strRequest);if ([strRequest hasSuffix:@"xbs_regist"]||[strRequest hasSuffix:@"xbs_refuse"]) {// 拦截点击链接if([strRequest hasSuffix:@"xbs_regist"]){self.block(YES);}else if([strRequest hasSuffix:@"xbs_refuse"]){self.block(NO);}// 不允许跳转decisionHandler(WKNavigationActionPolicyCancel);}else {// 允许跳转decisionHandler(WKNavigationActionPolicyAllow);}
}

 

==========参考WKWebView的使用========

http://www.cocoachina.com/articles/498556

http://blog.csdn.net/u011619283/article/details/52352514

http://www.cnblogs.com/jiang-xiao-yan/p/5345893.html

参考:http://www.jb51.net/article/107672.htm

http://www.jianshu.com/p/75f3abd40cc1

http://blog.csdn.net/y550918116j/article/details/50134625

http://blog.csdn.net/hmh007/article/details/53126809(综合包含jscore)

一、WKWebView Framework

WKWebView的14个类与3个协议:

WKBackForwardList:之前访问过的 web页面的列表,可以通过后退和前进动作来访问到。

WKBackForwardListItem: webview 中后退列表里的某一个网页。

WKFrameInfo: 包含一个网页的布局信息。

WKNavigation: 包含一个网页的加载进度信息。

WKNavigationAction:包含可能让网页导航变化的信息,用于判断是否做出导航变化。

WKNavigationResponse:包含可能让网页导航变化的返回内容信息,用于判断是否做出导航变化。

WKPreferences: 概括一个 webview的偏好设置。

WKProcessPool: 表示一个 web内容加载池。

WKUserContentController: 提供使用 JavaScript post信息和注射 script的方法。WKUserContentController可以理解为调度器,

WKScriptMessage: 包含网页发出的信息。WKScriptMessage则是携带的数据。

WKUserScript:表示可以被网页接受的用户脚本。

WKWebViewConfiguration: 初始化 webview的设置。

WKWindowFeatures: 指定加载新网页时的窗口属性。

WKWebsiteDataStore: 包含网页数据存储和查找。

 

===三个协议====

WKNavigationDelegate:提供了追踪主窗口网页加载过程和判断主窗口和子窗口是否进行页面加载新页面的相关方法。

WKUIDelegate:提供用原生控件显示网页的方法回调。这个协议主要用于WKWebView处理web界面的三种提示框(警告框、确认框、输入框),

WKScriptMessageHandler: 提供从网页中收消息的回调方法。WKScriptMessageHandler其实就是一个遵循的协议,它能让网页通过JS把消息发送给OC

 

 

二、WKWebView中的三个代理方法

1. 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;

 

//在发送请求之前,决定是否跳转

 

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;

2. WKUIDelegate

创建一个新的WKWebView

 

// 创建一个新的WebView

 

- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;

 

剩下三个代理方法全都是与界面弹出提示框相关的,针对于web界面的三种提示框(警告框、确认框、输入框)分别对应三种代理方法。

 

//界面弹出警告框

 

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void (^)())completionHandler;

 

//界面弹出确认框

 

- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;

 

//界面弹出输入框

 

- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *__nullable result))completionHandler;

 

3. WKScriptMessageHandler

这个协议中包含一个必须实现的方法,这个方法是native与web端交互的关键,它可以直接将接收到的JS脚本转为OC或Swift对象。

 

//从web界面中接收到一个js脚本时调用

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

 

三、使用WKWebView重写

这里我们和之前的界面做了一点改动,之前OC调用JS的时候是进行弹框处理,这里我在写的时候,很郁闷,方法可以调用过去,但是唯独js的alert方法调用没有效果,所以这里采用了输出到div的形式,并增加了一个clear按钮

WKWebView不支持nib文件,所以这里需要使用代码初始化并加载WebView

======实例1

WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];

config.preferences.minimumFontSize = 18;

self.wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0,self.view.bounds.size.width,self.view.bounds.size.height/2) configuration:config];

[self.view addSubview:self.wkWebView];

NSString *filePath = [[NSBundlemainBundle]pathForResource:@"index"ofType:@"html"];

NSURL *baseURL = [[NSBundlemainBundle]bundleURL];

 

[self.wkWebView loadHTMLString:[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil] baseURL:baseURL];

 

====实例2

 

//创建一个WKWebView的配置对象 
WKWebViewConfiguration *configur = [[WKWebViewConfiguration alloc] init]; 
//设置configur对象的preferences属性的信息 
WKPreferences *preferences = [[WKPreferences alloc] init]; 
configur.preferences = preferences; 
//是否允许与js进行交互,默认是YES的,如果设置为NO,js的代码就不起作用了 preferences.javaScriptEnabled = YES; 
/*设置configur对象的WKUserContentController属性的信息,也就是设置js可与webview内容交互配置 
1、通过这个对象可以注入js名称,在js端通过window.webkit.messageHandlers.自定义的js名称.postMessage(如果有参数可以传递参数)方法来发送消息到native;
2、我们需要遵守WKScriptMessageHandler协议,设置代理,然后实现对应代理方法(userContentController:didReceiveScriptMessage:); 
3、在上述代理方法里面就可以拿到对应的参数以及原生的方法名,我们就可以通过NSSelectorFromString包装成一个SEL,然后performSelector调用就可以了 
4、以上内容是WKWebview和UIWebview针对JS调用原生的方法最大的区别(UIWebview中主要是通过是否允许加载对应url的那个代理方法,通过在js代码里面写好特殊的url,然后拦截到对应的url,进行字符串的匹配以及截取操作,最后包装成SEL,然后调用就可以了) */ 
1、通过addScriptMessageHandler:name:方法,我们就可以注入js名称了,其实这个名称最好就是跟你的方法名一样,这样方便你包装使用,我这里自己写的就是openBigPicture,对应js中的代码就是window.webkit.messageHandlers.openBigPicture.postMessage() 
2、因为我的方法是有参数的,参数就是图片的url,因为点击网页中的图片,要调用原生的浏览大图的方法,所以你可以通过字符串拼接的方式给"openBigPicture"拼接成"openBigPicture:",我这里没有采用这种方式,我传递的参数直接是字典,字典里面放了方法名以及图片的url,到时候直接取出来用就可以了 
3、我的js代码中关于这块的代码是 window.webkit.messageHandlers.openBigPicture.postMessage({methodName:"openBigPicture:",imageSrc:imageArray[this.index].src}); 
4、js和原生交互这块内容离不开 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{}这个代理方法,这个方法以及参数说明请到下面方法对应处 */ 
WKUserContentController *userContentController = [[WKUserContentController alloc]init];
 [userContentController addScriptMessageHandler:self name:@"openBigPicture"];
 [userContentController addScriptMessageHandler:self name:@"openVideoPlayer"];
 configur.userContentController = userContentController; 
WKWebView *wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight) configuration:configur]; 
//WKWebview的estimatedProgress属性,就是加载进度,它是支持KVO监听进度改变的 
[wkWebView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil]; [self.contentView addSubview:wkWebView]; self.wkWebView = wkWebView; self.automaticallyAdjustsScrollViewInsets = NO; //设置内边距底部,主要是为了让网页最后的内容不被底部的toolBar挡着 wkWebView.scrollView.contentInset = UIEdgeInsetsMake(0, 0, 104, 0); 
//这句代码是让竖直方向的滚动条显示在正确的位置 wkWebView.scrollView.scrollIndicatorInsets = wkWebView.scrollView.contentInset;
wkWebView.UIDelegate = self; 
self.wkWebView.navigationDelegate = self; 

 

 

 

 

OC端:

 

//1. JS调用OC添加处理脚本

//添加消息处理,注意:self指代的是需要遵守WKScriptMessageHandler协议,结束时需要移除  

[userContentController addScriptMessageHandler:self name:@"showMobile"];

 

[userContentController addScriptMessageHandler:self name:@"showName"];

 

[userContentController addScriptMessageHandler:self name:@"showSendMsg"];

 

 

 

//从web界面中接收到一个js脚本时调用

 

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

     //name是js中的方法名,body是参数

 

1、js调用原生的方法就会走这个方法 2、message参数里面有2个参数我们比较有用,name和body, 2.1 :其中name就是之前已经通过addScriptMessageHandler:name:方法注入的js名称 2.2 :其中body就是我们传递的参数了,我在js端传入的是一个字典,所以取出来也是字典,

 

 

 

    if ([message.name isEqualToString:@"showMobile"]) {

    }

    

    if ([message.name isEqualToString:@"showName"]) {  

    }

    

    if ([message.name isEqualToString:@"showSendMsg"]) {

    }

    

}

 

//JS响应方法列表------在JS中实现

 

function btnClick1() {

    

    window.webkit.messageHandlers.showMobile.postMessage(null)

    

}

 

 

 

function btnClick2() {

    

    window.webkit.messageHandlers.showName.postMessage('www')

    

}

 

 

 

function btnClick3() {

    

    window.webkit.messageHandlers.showSendMsg.postMessage(['123', 'qqqq'])

}

 

 

 

 

 

//*************************OC调用JS的方法列表

- (IBAction)btnClick:(UIButton *)sender {

    

    if (!self.wkWebView.loading) {

        

        if (sender.tag == 123) {

            

            [self.wkWebView evaluateJavaScript:@"alertMobile()" completionHandler:^(id_Nullable response, NSError *_Nullable error) {

           

                NSLog(@"%@ %@",response,error);

                

            }];

            

        }

    

        if (sender.tag == 234) {

            

            [self.wkWebView evaluateJavaScript:@"alertName('小红')" completionHandler:nil];

            

        }

        

        

        

        if (sender.tag == 345) {

            

            [self.wkWebView evaluateJavaScript:@"alertSendMsg('18870707070','周末爬山真是件愉快的事情')"completionHandler:nil];

            

        }

       

    } else {

        

        NSLog(@"the view is currently loading content");

        

    }

    

}

 

 

function alertMobile() {

 

    document.getElementById('mobile').innerHTML = 'qqqqq';

    

}

 

 

 

function alertName(msg) {

   

    document.getElementById('name').innerHTML = 'sffd'

    

}

 

 

 

function alertSendMsg(num,msg) {

    

    document.getElementById('msg').innerHTML = '这是我的手机号:' + num + ',' + msg + '!!'

    

}

======实例3

 

1 WKScriptMessageHandler

 

1.1 WKScriptMessageHandler协议

 

WKScriptMessageHandler其实就是一个遵循的协议,它能让网页通过JS把消息发送给OC。其中协议方法。

 

/*! @abstract Invoked when a script message is received from a webpage.

 @param userContentController The user content controller invoking the

 delegate method.

 @param message The script message received.

 */

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

 

从协议中我们可以看出这里使用了两个类WKUserContentController和WKScriptMessage。WKUserContentController可以理解为调度器,WKScriptMessage则是携带的数据。

 

1.2 WKUserContentController

 

WKUserContentController有两个核心方法,也是它的核心功能。

 

- (void)addUserScript:(WKUserScript *)userScript;: js注入,即向网页中注入我们的js方法,这是一个非常强大的功能,开发中要慎用。

- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;:添加供js调用oc的桥梁。这里的name对应WKScriptMessage中的name,多数情况下我们认为它就是方法名。

1.3 WKScriptMessage

 

WKScriptMessage就是js通知oc的数据。其中有两个核心属性用的很多。

 

@property (nonatomic,readonly,copy) NSString *name;:对应- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;添加的name。

@property (nonatomic,readonly,copy)id body;:携带的核心数据。

js调用时只需

 

window.webkit.messageHandlers.<name>.postMessage(<messageBody>)

这里的name就是我们添加的name,是不是感觉很爽,就是这么简单,下面我们就来具体实现。

 

======2 JS调用OC

 

2.1 配置WKUserContentController

 

要想使用WKUserContentController为web页面添加桥梁,只需配置到WKWebViewConfiguration即可。

 

下面改造webView方法。

 

#pragma mark - get方法

- (WKWebView *)webView {

    if (_webView ==nil) {

        // js配置

        WKUserContentController *userContentController = [[WKUserContentController alloc] init];

        [userContentController addScriptMessageHandler:self name:@"jsCallOC"];

        

        // WKWebView的配置

        WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];

        configuration.userContentController = userContentController;

        

        // 显示WKWebView

        _webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];

        _webView.UIDelegate = self;//设置WKUIDelegate代理

        [self.view addSubview:_webView];

    }

    return _webView;

}

 

2.2 实现WKScriptMessageHandler

 

在当前页面引入WKScriptMessageHandler,并实现WKScriptMessageHandler协议即可。

 

@interface YJBaseVC () <WKScriptMessageHandler>

 

#pragma mark - WKScriptMessageHandler

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

    NSLog(@"方法名:%@", message.name);

    NSLog(@"参数:%@", message.body);

    // 方法名

    NSString *methods = [NSString stringWithFormat:@"%@:", message.name];

    SEL selector = NSSelectorFromString(methods);

    // 调用方法

    if ([self respondsToSelector:selector]) {

        [self performSelector:selector withObject:message.body];

    } else {

        NSLog(@"未实行方法:%@", methods);

    }

}

 

2.3 改造index.html页面

 

修改index.html的onClickButton()方法。

 

// 点击确定按钮

function onClickButton() {

    // 复杂数据

    var list = [1,2,3];

    var dict = {"name":"qqqq","qq":"222"};

    alert(dict);

    // JS通知WKWebView

    window.webkit.messageHandlers.jsCallOC.postMessage(dict);

}

 

这里我们为了测试效果传入了一个复杂的字典数据,而且字典中还有数组。input.value代表用户输入的数据。

 

这里使用了window.webkit.messageHandlers.jsCallOC.postMessage(dict);通知oc,jsCallOC这个属性就是前面我们通过WKUserContentController注入的。

 

2.4 测试交互

 

我们在viewDidLoad使用index.html页面完成测试。

 

- (void)viewDidLoad {

    [super viewDidLoad];

    //    [self loadWebView]; //加载测试

    NSURL *url = [[NSBundle mainBundle] URLForResource:@"index"withExtension:@"html"];

    NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];

    [self.webView loadRequest:urlRequest];//加载页面

}

=======WKUserScript预先注入一段js,在特定时刻加载js=======

 

4 WKUserScript JS注入

 

4.1 WKUserScript核心方法

 

在WebKit框架中,我们还可以预先添加JS方法,供其他人员调用。WKUserScript就是帮助我们完成JS注入的类,它能帮助我们在页面填充前或js填充完成后调用。核心方法。

 

/*! @abstract Returns an initialized user script that can be added to a@linkWKUserContentController @/link.

 @param source The script source.

 @param injectionTime When the script should be injected.

 @param forMainFrameOnly Whether the script should be injected into all frames or just the main frame.

 */

- (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly;

 

4.2 WKUserScriptInjectionTime枚举

 

在WKUserScriptInjectionTime枚举中有两个状态。

 

WKUserScriptInjectionTimeAtDocumentStart:js加载前执行。

WKUserScriptInjectionTimeAtDocumentEnd:js加载后执行。

4.3 js注入

 

WKUserScript的运行需依托WKUserContentController,接下来我们就为WKWebView注入一个js执行完毕后执行的alert方法。

 

改造- (WKWebView *)webView方法。

 

#pragma mark - get方法

- (WKWebView *)webView {

    if (_webView ==nil) {

        // js配置

        WKUserContentController *userContentController = [[WKUserContentController alloc] init];

        [userContentController addScriptMessageHandler:self name:@"jsCallOC"];

        // js注入,注入一个alert方法,页面加载完毕弹出一个对话框。

        NSString *javaScriptSource = @"alert(\"WKUserScript注入js\");";

        WKUserScript *userScript = [[WKUserScript alloc] initWithSource:javaScriptSource injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];// forMainFrameOnly:NO(全局窗口)yes(只限主窗口)

        [userContentController addUserScript:userScript];

        

        // WKWebView的配置

        WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];

        configuration.userContentController = userContentController;

        

        // 显示WKWebView

        _webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];

        [self.view addSubview:_webView];

    }

    return _webView;

}

******************************js调用OC====

 

#import "ViewController.h"

#import <WebKit/WebKit.h>

@interface ViewController ()<WKUIDelegate,WKNavigationDelegate,WKScriptMessageHandler>

 

@end

 

@implementation ViewController

 

- (void)viewDidLoad {

    [superviewDidLoad];

       self.edgesForExtendedLayout=UIRectEdgeNone;

    self.automaticallyAdjustsScrollViewInsets=NO;

    self.view.backgroundColor=[UIColorwhiteColor];

    NSString *htmlstr=@"<!DOCTYPE html>"

    "<html>"

    "<head>"

    "<meta charset='utf-8'>"

    "<title>菜鸟教程(runoob.com)</title>"

    "</head>"

    "<body>"

    "<h1>我的第一个标题</h1>"

    "<p>我的第一个段落。</p><script type='text/javascript'>"

    

"function hideOverlay(){"

    //注意postmessage中必须有参数

    "window.webkit.messageHandlers.hideOverlayLayout.postMessage('www');"

        "document.getElementsByTagName('h1')[0].innerHTML='ddd';"

    

    "}"

    "hideOverlay();"

    "</script>"

    "</body>";

    [selfsetTrainTicketWithMKwebHtmlStr:htmlstr];

}

 

 

 

//MKWebview

-(void)setTrainTicketWithMKwebHtmlStr:(NSString *)htmlStr{

    

    //    WKWebView *web=[[WKWebView alloc]initWithFrame:CGRectMake(0, 0,WIDTH , HEIGHT-64)];

    //    self.wkweb=web;

    //    web.UIDelegate=self;

    //    web.navigationDelegate=self;

    //    [self.view addSubview:web];

    //    [web loadHTMLString:htmlStr baseURL:nil];

    

    

    //创建一个WKWebView的配置对象

    WKWebViewConfiguration *configur = [[WKWebViewConfigurationalloc]init];

    

    //设置configur对象的preferences属性的信息

    WKPreferences *preferences = [[WKPreferencesalloc]init];

    configur.preferences = preferences;

    

    //是否允许与js进行交互,默认是YES的,如果设置为NO,js的代码就不起作用了

    preferences.javaScriptEnabled =YES;

    

    WKUserContentController *userContentController = [[WKUserContentControlleralloc]init];

    //添加消息处理,注意:self指代的是需要遵守WKScriptMessageHandler协议,结束时需要移除

    [userContentController addScriptMessageHandler:selfname:@"hideOverlayLayout"];

    

    configur.userContentController = userContentController;

    

    WKWebView *wkWebView = [[WKWebViewalloc]initWithFrame:CGRectMake(0, 64,375 , 667-64)configuration:configur];

    

    [self.viewaddSubview:wkWebView];

    

    //设置内边距底部,主要是为了让网页最后的内容不被底部的toolBar挡着

    wkWebView.scrollView.contentInset =UIEdgeInsetsMake(0, 0, 104, 0);

    //这句代码是让竖直方向的滚动条显示在正确的位置

    wkWebView.scrollView.scrollIndicatorInsets = wkWebView.scrollView.contentInset;

    

    wkWebView.UIDelegate =self;

    

    wkWebView.navigationDelegate =self;

    

    [wkWebView loadHTMLString:htmlStrbaseURL:nil];

}

 

-(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecifiedWKNavigation *)navigation {

    

}

 

 

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

{

    //NSLog(@"%@,%@",message.name,message.body);

    

    NSString *coverWebview=message.name;

    NSLog(@"js中的方法 ---%@",coverWebview);

//实现方法一

    //如果方法名是我们需要的,那么说明是时候调用原生对应的方法了

    if([coverWebviewisEqualToString:@"hideOverlayLayout"]){

    

        [self hideOverlayLayout];

    }

 /*

//实现方法二:

//    SEL selector = NSSelectorFromString(coverWebview);

//        NSLog(@"js方法执行了");

//        //判断当前方法是否存在

//        if ([self respondsToSelector:selector]) {

//#pragma clang diagnostic push

//#pragma clang diagnostic ignored"-Warc-performSelector-leaks"

//            //写在这个中间的代码,都不会被编译器提示PerformSelector may cause a leak because its selector is unknown类型的警告

//    [self performSelector:selector withObject:nil];

//#pragma mark diagnostic pop

//        

//        }

    */

}

 

-(void)hideOverlayLayout{

    NSLog(@"js调用OC方法实现了");

 

}

 

 

===========WKWebView中的cookie的设置===

 

 

WKWebView 的Cookie问题

UIWebView中会自动保存Cookie,如果登录了一次,下次再次进入的时候,会记住登录状态

而在WKWebView中,并不会这样,WKWebView在初始化的时候有一个方法

- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration

通过这个方法,设置 configuration让WKWebView知道登录状态,configuration可以通过已有的Cookie进行设置,也可以通过保存上一次的configuration进行设置

***实例1

WKWebView * webView = /*set up your webView*/

NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com/index.html"]];

[request addValue:@"TeskCookieKey1=TeskCookieValue1;TeskCookieKey2=TeskCookieValue2;"forHTTPHeaderField:@"Cookie"];

[webView loadRequest:request];

 

******实例2

WKUserContentController* userContentController = WKUserContentController.new;

WKUserScript * cookieScript = [[WKUserScriptalloc]

                               initWithSource:@"document.cookie = 'TeskCookieKey1=TeskCookieValue1';

document.cookie = 'TeskCookieKey2=TeskCookieValue2';"

           injectionTime:WKUserScriptInjectionTimeAtDocumentStartforMainFrameOnly:NO];

[userContentController addUserScript:cookieScript];

WKWebViewConfiguration* webViewConfig =WKWebViewConfiguration.new;

webViewConfig.userContentController = userContentController;

WKWebView * webView = [[WKWebView alloc] initWithFrame:CGRectMake(/*set your values*/) configuration:webViewConfig];

 

 

 

**************************JS调用OC之WKWebView + 第三方框架WebViewJavascriptBridge========

参考:https://github.com/Haley-Wong/JS_OC

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

==========1.在即使中要实现的代码:

 

function setupWebViewJavascriptBridge(callback) {

    if (window.WebViewJavascriptBridge) {return callback(WebViewJavascriptBridge); }

    if (window.WVJBCallbacks) {return window.WVJBCallbacks.push(callback); }

    window.WVJBCallbacks = [callback];

    var WVJBIframe = document.createElement('iframe');

    WVJBIframe.style.display = 'none';

    WVJBIframe.src = 'https://__bridge_loaded__';

    document.documentElement.appendChild(WVJBIframe);

    setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)

}

 

<!--处理交互 方法名要和ios内定义的对应-->

setupWebViewJavascriptBridge(function(bridge) {

    

    <!--处理 oc调用 js -->

    bridge.registerHandler('registerAction', function(data, responseCallback) {

        //处理oc给的传参

        alert('oc请求js 传值参数是:'+data)

        var responseData = { 'result':'handle success' }

        //处理完,回调传值给oc

        responseCallback(responseData)

    })

    

    var callbackButton = document.getElementById('buttons').appendChild(document.createElement('button'))

    callbackButton.innerHTML = '点击我,我会调用oc的方法'

    callbackButton.onclick = function(e) {

        e.preventDefault()

        <!--处理 js调用 oc -->

        bridge.callHandler('loginAction', {'userId':'zhangsan','name': '章三'}, function(response) {

            //处理oc过来的回调

            alert('收到oc过来的回调:'+response)

        })

    }

})

 

=====2.需要在OC中实现的代码

 

 

******OC代码

 

- pod导入框架

 

pod 'WebViewJavascriptBridge'

 

#import "WebViewJavascriptBridge.h"

在 viewDidload 中:

//初始化  WebViewJavascriptBridge

if (_bridge) {return; }

[WebViewJavascriptBridge enableLogging];

_bridge = [WebViewJavascriptBridge bridgeForWebView:webView];

[_bridge setWebViewDelegate:self];

 

//请求加载html,注意:这里h5加载完,会自动执行一个调用oc的方法

[self loadExamplePage:webView];

 

//申明js调用oc方法的处理事件,这里写了后,h5那边只要请求了,oc内部就会响应

[self JS2OC];

 

//模拟操作:2秒后,oc会调用js的方法

//注意:这里厉害的是,我们不需要等待html加载完成,就能处理oc的请求事件;此外,webview的request也可以在这个请求后面执行(可以把上面的[self loadExamplePage:webView]放到[self OC2JS]后面执行,结果是一样的)

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 *NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

    [self OC2JS];

});

 

***********************************************JS 调用  OC

-(void)JS2OC{

    /*

     含义:JS调用OC

     @param registerHandler 要注册的事件名称(比如这里我们为loginAction)

     @param handel 回调block函数当后台触发这个事件的时候会执行block里面的代码

     */

   [_bridge registerHandler:@"loginAction" handler:^(id data, WVJBResponseCallback responseCallback) {

       // data js页面传过来的参数 假设这里是用户名和姓名,字典格式

        NSLog(@"JS调用OC,并传值过来");

        

        //利用data参数处理自己的逻辑

        NSDictionary *dict = (NSDictionary *)data;

        NSString *str = [NSString stringWithFormat:@"用户名:%@ 姓名:%@",dict[@"userId"],dict[@"name"]];

        [self renderButtons:str];

        

        // responseCallback给js的回复

        responseCallback(@"报告,oc已收到js的请求");

    }];

    

}

 

 ***************************************** OC 调用  JS

 

-(void)OC2JS{

    /*

     含义:OC调用JS

     @param callHandler 商定的事件名称,用来调用网页里面相应的事件实现

     @param data id类型,相当于我们函数中的参数,向网页传递函数执行需要的参数

    注意,这里callHandler分3种,根据需不需要传参数和需不需要后台返回执行结果来决定用哪个

     */

 

//---不带回调[_bridge callHandler:@"registerAction" data:@"我是oc请求js的参数"];

//带回调

    [_bridge callHandler:@"registerAction" data:@"uid:123 pwd:123" responseCallback:^(id responseData) {

        NSLog(@"oc请求js后接受的回调结果:%@",responseData);

    }];

    

}

这篇关于iOS之JavaScript与OC的相互调用:WKwebview 的使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1017725

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数