一、WebView的相关简单操作
self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
self.webView.delegate = self;
[self.view addSubview:self.webView];
NSURL *url = [[NSBundle mainBundle] URLForResource:@"index.html" withExtension:nil];
NSURLRequest *reqeust = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:reqeust];
#pragma mark - UIWebViewDelegate
// 加载所有请求数据,以及控制是否加载
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSLog(@"request = %@",request);
// 路由能力 --
NSLog(@"URL = %@",request.URL);
NSLog(@"scheme = %@",request.URL.scheme);
NSString *scheme = request.URL.scheme;
if ([scheme isEqualToString:@"heheorg"])
{
return NO;//可以根据要跳转的scheme设置不往下面下载,在此拦截住一个路由
}
// 说明在此可以为所欲为,只要我有一套自己制定的方案
// request.URL.pathComponents 得到 oc 的方法和参数
/*例子:
NSArray *array = request.URL.pathComponents;
// 此处伪代码没有做 非空处理 - 实际若没有非空处理则很容易出bug
NSString *methodName = array[1];
if ([methodName isEqualToString:@"getSum"])
{
// array[2],array[3]
NSLog(@"%@-%@",array[2],array[3]);
}
[self performSelector:NSSelectorFromString(methodName) withObject:array afterDelay:0];
*/
return YES;
}
// 开始加载
- (void)webViewDidStartLoad:(UIWebView *)webView
{
// 进度条
NSLog(@"****************分界线****************");
NSLog(@"开始加载");
}
// 加载完成
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
// tittle
NSString *tittle = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
self.title = tittle;
NSLog(@"****************分界线****************");
NSLog(@"加载完成");
}
// 加载失败
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
NSLog(@"****************分界线****************");
NSLog(@"加载失败:%@",error);
}
// oc调用js方法
- (IBAction)didClickRightItemClick:(id)sender
{
NSLog(@"响应按钮");
// 可用于刷新页面等功能
// 客户端信息传入token
// js 需要加密等处理 -> 返回data(有可能是异步,所以可以返回一个函数)
// 异步 用于OC加密 ,oc加密完成后 JS才可以展示 (此处无例子)
// 混合开发
// 重定向 - H5 3次
NSString *str = [self.webView stringByEvaluatingJavaScriptFromString:@"showAlert('呵呵')('哈哈')"];
NSLog(@"%@",str);// js方法返回的数据
/*
js 代码:
<script>
function showAlert(name)
{
alert(name);
return function handle(str)
{
alert('我是一个弹窗'+name+str);
return name + '返回给OC';
}
}
</script>
*/
}
二、JavaScriptCore
需导入JavaScriptCore.framework
1、三个关键类
-
JSContext.h提供了全局的上下文
-
JSValue.h主要是一些类型的强转(jsvalue转化成oc类型)
-
JSExport.h主要用在js响应oc对象,通过实现协议达到的映射来使用
2、初级使用
- (void)viewDidLoad
{
[super viewDidLoad];
self.webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
self.webView.delegate = self;
[self.view addSubview:self.webView];
NSURL *url = [[NSBundle mainBundle] URLForResource:@"index.html" withExtension:nil];;
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[self.webView loadRequest:request];
}
#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"];
[jsContext evaluateScript:@"var arr = ['doomfist','hehe','呵呵']"];// 定义全局变量
// evaluateScript 可执行一段脚本
// 清晰 --
// js代码中没有showMessage函数,但js调用了showMessage函数
// 类似于kvc的形式,key为showMessage,value为^{}
// 所以此处可以定义一个showMessage函数,一旦注册就会覆盖js代码中的同名方法
// 保存一段代码块
jsContext[@"showMessage"] = ^{
NSLog(@"来了");
// 参数 (JS 带过来的)
NSArray *args = [JSContext currentArguments];
NSLog(@"args = %@",args);
};
/*
js代码:
function showAlert()
{
alert("hello");
showMessage("点击了弹框按钮...",arr);
}
*/
}
3、JavaScriptCore - 用于解释oc和js,是oc和js的桥梁
Js的windows中的值通过JavaScriptCore传入oc的self.windows的runloop中变成jsValue类型
- (void)setupJS
{
// JS-OC
self.jsContext[@"showMessage"] = ^{
NSLog(@"来了");
// 参数 (JS 带过来的)
NSArray *args = [JSContext currentArguments];
NSLog(@"args = %@",args);
NSLog(@"currentThis = %@",[JSContext currentThis]);
// 能够监控当前Windows信息 - 没什么用
NSLog(@"currentCallee = %@",[JSContext currentCallee]);
// 当前的闭包的数据 - 用处不大
// OC-JS
NSDictionary *dict = @{@"name":@"呵呵",@"age":@18};
[[JSContext currentContext][@"ocCalljs"] callWithArguments:@[dict,@"呵呵哒"]];
// 调方法并传参
};
//JS-OC
self.jsContext[@"showDict"] = ^{
NSLog(@"来了");
// 参数 (JS 带过来的)
NSArray *args = [JSContext currentArguments];
NSLog(@"args = %@",args);
};
}
/* js代码
function ocCalljs(dict,str)
{
var name = dict['name'];
var age = dict['age'];
alert(name + age + str);
// 传回去
showDict(dict)
}
*/
4、JS操作对象
// JS 操作对象
JSObject *Object = [[JSObject alloc] init];
self.jsContext[@"Object"] = Object;
NSLog(@"Object == %d",[Object getSum:20 num2:40]);
/* js代码
function openAlbumImage()
{
getImage();
Object.letShowImage();
alert(Object.getS(10,30));
}
*/
// ---------
<JSExport>
- (void)letShowImage;
// 协议 - 协议方法
JSExportAs(getS, -(int)getSum:(int)num1 num2:(int)num2);
// ---------
- (void)letShowImage
{
NSLog(@"打开相册,上传图片");
}
- (int)getSum:(int)num1 num2:(int)num2
{
NSLog(@"来了");
// int (nil) - jsvalue (0)
return num1+num2;
}
// ---------
三、WKWebview基本用法
//加载
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
// 在这里插入下面的部分↓
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];
// 这里的内容插入上面↑ 如不插入则不会显示手机应该显示的正常页面
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;
// 交互
- (IBAction)didClickRightItemAction:(id)sender
{
// 调方法,传参数
NSString *jsStr = [NSString stringWithFormat:@"showAlert('%@')",@"登陆成功"];
// oc调用js 直接执行脚本
[self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@----%@",result, error);
}];
}
// 代理
#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 - WKNavigationDelegate
// 加载完成的代理
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
{
self.title = webView.title;
NSString *jsStr2 = @"var arr = [3, 'hehe', 'abc']; ";
[self.webView evaluateJavaScript:jsStr2 completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@----%@",result, error);
}];
}
// 拦截
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
NSURL *URL = navigationAction.request.URL;
NSString *scheme = [URL scheme];
if ([scheme isEqualToString:@"hehe"])
{
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);
}
// 另一种oc调用js
// 一定要remove,不然会循环引用
// self - webView - configuration - userContentController - self
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"messgaeOC"];
// addScriptMessageHandler此处注册WKScriptMessageHandler代理
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"messgaeOC"];
}
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
if (message.name)
{
// OC 层面的消息
}
NSLog(@"message == %@ --- %@",message.name,message.body);
}
/* js代码
function messgaeHandle()
{
window.webkit.messageHandlers.messgaeOC.postMessage("hehe 消息");
}
*/
附:
wkwebview的代理的常用方法
//#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);
四、WebViewJavaScriptBridge
self.wjb = [WebViewJavascriptBridge bridgeForWebView:self.webView];
// 如果你要在VC中实现 UIWebView的代理方法 就实现下面的代码(否则省略)
[self.wjb setWebViewDelegate:self];
// 需要前端(js)适配
[self.wjb registerHandler:@"jsCallsOC" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"currentThread == %@",[NSThread currentThread]);
NSLog(@"data == %@ -- %@",data,responseCallback);
}];
// 内部逻辑
+ (instancetype)bridge:(id)webView
{
#if defined supportsWKWebView
if ([webView isKindOfClass:[WKWebView class]])
{
return (WebViewJavascriptBridge*) [WKWebViewJavascriptBridge bridgeForWebView:webView];
}
#endif
if ([webView isKindOfClass:[WVJB_WEBVIEW_TYPE class]])
{
WebViewJavascriptBridge* bridge = [[self alloc] init];
[bridge _platformSpecificSetup:webView];
return bridge;
}
[NSException raise:@"BadWebViewType" format:@"Unknown web view type."];
return nil;
}
- (IBAction)didClickLeftAction:(id)sender
{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self.wjb callHandler:@"OCCallJSFunction" data:@"oc调用JS咯" responseCallback:^(id responseData) {
NSLog(@"currentThread == %@",[NSThread currentThread]);
NSLog(@"调用完JS后的回调:%@",responseData);
}];
});
}