OC与JS交互
JS与OC交互初探
webView http wkwebView webKit 苹果内核
- UIWebView
#pragma mark - private
- (void)getSum{
NSLog(@"123344444");
NSString *result = [self.webView stringByEvaluatingJavaScriptFromString:@"showAlert('HELLO')"];
NSLog(@"result == %@",result);
}
#pragma mark - 响应
- (IBAction)didClickLetfAction:(id)sender {
}
- (IBAction)didClickRightItemClick:(id)sender {
NSLog(@"响应按钮");
NSString *result = [self.webView stringByEvaluatingJavaScriptFromString:@"showAlert('HELLO')()"];
NSLog(@"result == %@",result);
}
#pragma mark - UIWebViewDelegate
/**
这些都是JS响应的样式
UIWebViewNavigationTypeLinkClicked, 点击
UIWebViewNavigationTypeFormSubmitted, 提交
UIWebViewNavigationTypeBackForward, 返回
UIWebViewNavigationTypeReload, 刷新
UIWebViewNavigationTypeFormResubmitted, 重复提交
UIWebViewNavigationTypeOther 其他
*/
// 加载所有请求数据,以及控制是否加载
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
NSLog(@"%@",request.URL.scheme); // 标识 我们自己协议
NSLog(@"%@",request.URL.host); // 方法名
NSLog(@"%@",request.URL.pathComponents); // 参数
// JS 调用OC 的原理就是 拦截URL
NSString *scheme = request.URL.scheme;
if ([scheme isEqualToString:@"tzedu"]) {
NSLog(@"来了,我们自定义的协议");
NSArray *args = request.URL.pathComponents;
NSString *methodName = args[1];
// 方法1
// if ([methodName isEqualToString:@"getSum"]) {
// [self getSum];
// }
// 方法2
SEL methodSel = NSSelectorFromString(methodName);
if ([self respondsToSelector:methodSel]) {
#pragma clang diagnostic push
// 让编译器忽略错误
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
// 让编译器出栈,恢复状态,继续编译后续的代码!
[self performSelector:methodSel withObject:args[2]];
#pragma clang diagnostic pop
}
// 方法3
// objc_msgSend(self,methodSel,args[2],args[3]);
((void (*) (id, SEL, id))(objc_msgSend))(self, methodSel,args[2]);
// 定义kc_msgSend 函数指针 (void *) 替代了 (void (*)(id, SEL, id))
void (*kc_msgSend) (id, SEL, id) = (void *)objc_msgSend;
kc_msgSend(self,methodSel,args[2]);
return NO;
}
return YES;
}
// 开始加载
- (void)webViewDidStartLoad:(UIWebView *)webView{
NSLog(@"****************华丽的分界线****************");
NSLog(@"开始加载咯!!!!");
}
// 加载完成
- (void)webViewDidFinishLoad:(UIWebView *)webView{
//死的!!!
NSString *titlte = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
self.title = titlte;
NSLog(@"****************华丽的分界线****************");
NSLog(@"加载完成了咯!!!!");
}
// 加载失败
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
NSLog(@"****************华丽的分界线****************");
NSLog(@"加载失败了咯,为什么:%@",error);
}
JSCore
//上下文
#import "JSContext.h"
//把js数据类型转成oc
#import "JSValue.h"
//提供底层资源
#import "JSManagedValue.h"
//Machine信息
#import "JSVirtualMachine.h"
//协议,js调用OC,oc只要实现这个协议就好了
#import "JSExport.h"
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>
@protocol KCProtocol
- (void)letShowImage;
JSExportAs(getSum, -(int)getSum:(int)num1 num2:(int)num2);
@end
@interface KC_JSObject : NSObject
@end
#import "KC_JSObject.h"
@implementation KC_JSObject
- (void)letShowImage{
NSLog(@"打开相册,上传图片");
}
- (int)getSum:(int)num1 num2:(int)num2{
return num1+num2;
}
@end
#import "JSCoreMoreVC.h"
#import "KC_JSObject.h"
@interface JSCoreMoreVC ()<UIWebViewDelegate,UIImagePickerControllerDelegate,UINavigationControllerDelegate>
@property (nonatomic, strong) UIWebView *webView;
@property (nonatomic, strong) UILabel *showLabel;
@property (nonatomic, strong) JSContext *jsContext;
@property (nonatomic,strong) UIImagePickerController *imagePicker;
@end
@implementation JSCoreMoreVC
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:self.showLabel];
self.webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 124, self.view.bounds.size.width, self.view.bounds.size.height-124)];
self.webView.delegate = self;
[self.view addSubview:self.webView];
NSURL *url = [[NSBundle mainBundle] URLForResource:@"index2.html" withExtension:nil];;
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[self.webView loadRequest:request];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"点我" style:UIBarButtonItemStyleDone target:self action:@selector(didClickRightAction)];
}
- (void)didClickRightAction{
NSLog(@"rightBarButtonItem");
}
#pragma mark - UIWebViewDelegate
// 加载完成
- (void)webViewDidFinishLoad:(UIWebView *)webView{
NSString *titlt = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
self.title = titlt;
//JSContext就为其提供着运行环境 H5上下文
JSContext *jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
self.jsContext = jsContext;
__weak typeof(self) weakSelf = self;
// 异常处理
self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception) {
context.exception = exception;
NSLog(@"exception == %@",exception);
NSLog(@"%@",context);
};
// 提供全局变量
[self.jsContext evaluateScript:@"var arr = [3, 'Cooci', 'abc'];"];
self.jsContext[@"showMessage"] = ^() {
JSValue *thisValue = [JSContext currentThis];
NSLog(@"thisValue = %@",thisValue);
JSValue *cValue = [JSContext currentCallee];
NSLog(@"cValue = %@",cValue);
NSArray *args = [JSContext currentArguments];
NSLog(@"来了:%@",args);
NSDictionary *dict = @{@"name":@"cooci",@"age":@18};
[[JSContext currentContext][@"ocCalljs"] callWithArguments:@[dict]];
};
// 因为是全局变量 可以直接获取
JSValue *arrValue = self.jsContext[@"arr"];
NSLog(@"arrValue == %@",arrValue);
//纠正用法
// JSValue *value = [JSValue valueWithObject:@"test“ inContext:context];
// JSManagedValue *managedValue = [JSManagedValue managedValueWithValue:value andOwner:self];
self.jsContext[@"showDict"] = ^(JSValue *value) {
NSArray *args = [JSContext currentArguments];
JSValue *dictValue = args[0];
NSDictionary *dict = dictValue.toDictionary;
NSLog(@"%@",dict);
// 模拟用
int num = [[arrValue.toArray objectAtIndex:0] intValue];
num += 10;
NSLog(@"arrValue == %@ num == %d",arrValue.toArray,num);
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.showLabel.text = dict[@"name"];
});
};
//异常收集
self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception) {
weakSelf.jsContext.exception = exception;
NSLog(@"exception == %@",exception);
};
// JS 操作对象
KC_JSObject *kcObject = [[KC_JSObject alloc] init];
self.jsContext[@"kcObject"] = kcObject;
// 打开相册
self.jsContext[@"getImage"] = ^() {
weakSelf.imagePicker = [[UIImagePickerController alloc] init];
weakSelf.imagePicker.delegate = weakSelf;
weakSelf.imagePicker.allowsEditing = YES;
weakSelf.imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[weakSelf presentViewController:weakSelf.imagePicker animated:YES completion:nil];
};
}
- (void)dealloc{
NSLog(@"dealloc :走了");
}
#pragma mark -- UIImagePickerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
NSLog(@"info---%@",info);
UIImage *resultImage = [info objectForKey:@"UIImagePickerControllerEditedImage"];
NSData *imgData = UIImageJPEGRepresentation(resultImage, 0.01);
NSString *encodedImageStr = [imgData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
[self removeSpaceAndNewline:encodedImageStr];
[self dismissViewControllerAnimated:YES completion:nil];
NSString *imageString = [self removeSpaceAndNewline:encodedImageStr];
NSString *jsFunctStr = [NSString stringWithFormat:@"showImage('%@')",imageString];
[self.jsContext evaluateScript:jsFunctStr];
}
- (NSString *)removeSpaceAndNewline:(NSString *)str
{
NSString *temp = [str stringByReplacingOccurrencesOfString:@" " withString:@""];
temp = [temp stringByReplacingOccurrencesOfString:@"\r" withString:@""];
temp = [temp stringByReplacingOccurrencesOfString:@"\n" withString:@""];
return temp;
}
- (void)openAlbum{
self.imagePicker = [[UIImagePickerController alloc] init];
self.imagePicker.delegate = self;
self.imagePicker.allowsEditing = YES;
self.imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentViewController:self.imagePicker animated:YES completion:nil];
}
#pragma mark - lazy
- (UILabel *)showLabel{
if (!_showLabel) {
_showLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 64+20, 100, 30)];
_showLabel.textColor = [UIColor orangeColor];
_showLabel.font = [UIFont systemFontOfSize:16];
_showLabel.text = @"我是一个文本";
}
return _showLabel;
}
@end
WKWView交互
- (void)viewDidLoad {
[super viewDidLoad];
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
//屏幕比例配置
NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
WKUserContentController *wkUController = [[WKUserContentController alloc] init];
[wkUController addUserScript:wkUScript];
configuration.userContentController = wkUController;
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
self.webView.navigationDelegate = self;
self.webView.UIDelegate = self;
[self.view addSubview:self.webView];
NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"index.html" ofType:nil];
NSURL *fileURL = [NSURL fileURLWithPath:urlStr];
[self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL];
}
- (IBAction)didClickLeftAction:(id)sender {
[self.navigationController pushViewController:[WKMessageHandleVC new] animated:YES];
}
- (IBAction)didClickRightItemAction:(id)sender {
NSString *jsStr = [NSString stringWithFormat:@"showAlert('%@')",@"登陆成功"];
[self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@----%@",result, error);
}];
}
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
// 拦截 -- 超链接 -- 自定义
NSURL *URL = navigationAction.request.URL;
NSString *scheme = [URL scheme];
if ([scheme isEqualToString:@"tzedu"]) {
NSString *host = [URL host];
if ([host isEqualToString:@"jsCallOC"]) {
NSMutableDictionary *temDict = [self decoderUrl:URL];
NSString *username = [temDict objectForKey:@"username"];
NSString *password = [temDict objectForKey:@"password"];
NSLog(@"%@---%@",username,password);
}else{
NSLog(@"不明地址 %@",host);
}
//取消掉。实现拦截
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
self.title = webView.title;
}
#pragma mark - WKUIDelegate
- (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:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}]];
[self presentViewController:alert animated:YES completion:nil];
}
#pragma mark - 解析URL地址
- (NSMutableDictionary *)decoderUrl:(NSURL *)URL{
NSArray *params =[URL.query componentsSeparatedByString:@"&"];
NSMutableDictionary *tempDic = [NSMutableDictionary dictionary];
for (NSString *paramStr in params) {
NSArray *dicArray = [paramStr componentsSeparatedByString:@"="];
if (dicArray.count > 1) {
NSString *decodeValue = [dicArray[1] stringByRemovingPercentEncoding];
[tempDic setObject:decodeValue forKey:dicArray[0]];
}
}
return tempDic;
}
#pragma mark - 大小适应
- (WKUserContentController *)wkwebViewScalPreferences{
NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
WKUserContentController *wkUController = [[WKUserContentController alloc] init];
[wkUController addUserScript:wkUScript];
// WKPreferences *preferences = [[WKPreferences alloc] init];
// preferences.javaScriptCanOpenWindowsAutomatically = YES;
// preferences.minimumFontSize = 100.0;
// configuration.preferences = preferences;
return wkUController;
}
#pragma mark - WKNavigationDelegate
//请求之前,决定是否要跳转:用户点击网页上的链接,需要打开新页面时,将先调用这个方法。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
//接收到相应数据后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
//页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation;
// 主机地址被重定向时调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation;
// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation;
// 页面加载完毕时调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation;
//跳转失败时调用
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;
// 如果需要证书验证,与使用AFN进行HTTPS证书验证是一样的
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler;
//9.0才能使用,web内容处理中断时会触发
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView NS_AVAILABLE(10_11, 9_0);
@end
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//循环引用self->web->config->self
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"messgaeOC"];
}
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
//解决循环引用
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"messgaeOC"];
}
#pragma mark - didClickRightAction
- (void)didClickRightAction{
NSLog(@"didClickRightAction");
NSString *jsStr2 = @"showAlert('messageHandle:OC-->JS')";
[self.webView evaluateJavaScript:jsStr2 completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@----%@",result, error);
}];
}
#pragma mark - WKScriptMessageHandler。执行脚本必须实现
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
NSLog(@"message == %@ --- %@",message.name,message.body);
// 展示不出来 -- 会打开 : iframe为0
// if (message.name) {
// <#statements#>
// }
}
#pragma mark - WKUIDelegate
- (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:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}]];
[self presentViewController:alert animated:YES completion:nil];
}
WebScriptBridge
- (void)viewDidLoad {
[super viewDidLoad];
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
WKUserContentController *wkUController = [[WKUserContentController alloc] init];
[wkUController addUserScript:wkUScript];
configuration.userContentController = wkUController;
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
self.webView.UIDelegate = self;
[self.view addSubview:self.webView];
NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"index.html" ofType:nil];
NSURL *fileURL = [NSURL fileURLWithPath:urlStr];
[self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL];
//绑定一个视图。UI/WK都可以。
self.wjb = [WebViewJavascriptBridge bridgeForWebView:self.webView];
// 如果你要在VC中实现 UIWebView的代理方法 就实现下面的代码(否则省略)
[self.wjb setWebViewDelegate:self];
//js调用OC
[self.wjb registerHandler:@"jsCallsOC" handler:^(id data, WVJBResponseCallback responseCallback) {
//回来就是主线程
NSLog(@"currentThread == %@",[NSThread currentThread]);
//直接回调data
//responseCallback可以继续跟踪
NSLog(@"data == %@ -- %@",data,responseCallback);
}];
}
- (IBAction)didClickLeftAction:(id)sender {
// // 如果不需要参数,不需要回调,使用这个
[self.wjb callHandler:@"OCCallJSFunction"];
// // 如果需要参数,不需要回调,使用这个
[self.wjb callHandler:@"OCCallJSFunction" data:@"一个字符串"];
// 如果既需要参数,又需要回调,使用这个
[self.wjb callHandler:@"OCCallJSFunction" data:@"oc调用JS咯" responseCallback:^(id responseData) {
NSLog(@"currentThread == %@",[NSThread currentThread]);
NSLog(@"调用完JS后的回调:%@",responseData);
}];
}