iOS与HTML混编,最好的例子就是网易新闻详情页的实现,图片与文字可以根据服务器返回的数据随意排版,很方便
本文主要讲WKWebView,与UIWebView和JavaScriptCore孰优孰劣我就不比较了,
网易新闻页实现流程我就不重复写了,还有其他好的文章放在最后,
但感觉大多说的不细,对没有web开发经验的来说有点难懂,所以本文会把我写的一些简单的js代码拿出来,对没有web基础的来说更加清晰
本文的基础知识主要为:
1.点击html按钮,oc的控制器dismiss掉
2.点击oc按钮,js切换页面
3.点击html按钮,js切换页面
4.点击html的div标签,js调用oc来present一个新的controller,上面有个返回按钮,点击可以返回webView的控制器
点击获取源代码
效果图就不放了,创建几个文件把代码全部复制一下就可以看到效果了
ViewController中有个按钮,点击即可跳转到WebVC.
WebVC的.m文件,其中值得注意的是WKDelegate,是解决WKWebView循环引用问题的代理
#import "WebVC.h"
#import <WebKit/WebKit.h>
#import "WKDelegateController.h"
@interface WebVC ()<WKUIDelegate,WKNavigationDelegate,WKDelegate>
@property (strong, nonatomic) WKWebView *webView;
@property (strong, nonatomic) WKUserContentController *userContent;
@property (weak, nonatomic) UIButton *backBtn;
@end
@implementation WebVC
- (void)dealloc
{
NSLog(@"无循环引用");
//这里需要注意,前面增加过的方法一定要remove掉。
//addScriptMessageHandler要和removeScriptMessageHandlerForName配套出现,否则会造成内存泄漏。
[self.userContent removeScriptMessageHandlerForName:@"ocMethod"];
[self.userContent removeScriptMessageHandlerForName:@"presentMethod"];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor lightGrayColor];
//创建配置
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
//创建UserContentController(提供javaScript向webView发送消息的方法)
self.userContent = [[WKUserContentController alloc] init];
//添加消息处理,
//注意: addScriptMessageHandler后面的参数指代的是需要遵守WKScriptMessageHandler协议,结束时需要移除
//但无论在哪里移除都会发现dealloc并不会执行,这样肯定是不行的,会造成内存泄漏
//试了下用weak指针还是不能释放,不知道是什么原因
//因此参考百度上的写法是用一个新的controller来处理,新的controller再绕用delegate绕回来,即使没移除也会走dealloc了
WKDelegateController * delegateController = [[WKDelegateController alloc]init];
delegateController.delegate = self;
[self.userContent addScriptMessageHandler:delegateController name:@"ocMethod"];
[self.userContent addScriptMessageHandler:delegateController name:@"presentMethod"];
//将UserContent设置到配置文件中
config.userContentController = self.userContent;
//配置webView
self.webView = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:config];
self.webView.UIDelegate = self;
self.webView.navigationDelegate = self;
[self.view addSubview:self.webView];
//加载本地html
NSURL *url = [[NSBundle mainBundle] URLForResource:@"index" withExtension:@"html"];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:urlRequest];
//添加一个返回按钮
UIButton *backBtn = [[UIButton alloc]initWithFrame:CGRectMake(10, 35, 50, 50)];
[backBtn setTitle:@"返回" forState:UIControlStateNormal];
[backBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[backBtn addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
self.backBtn = backBtn;
[self.webView addSubview:backBtn];
}
//这里就是使用高端配置,js调用oc的处理地方。我们可以根据name和body,进行桥协议的处理。
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
NSString *messageName = message.name;
if ([@"ocMethod" isEqualToString:messageName])
{
id messageBody = message.body;
NSLog(@"%@",messageBody);
//点击html按钮,让当前webView页面dismiss掉
[self dismissViewControllerAnimated:YES completion:nil];
}
else if([@"presentMethod" isEqualToString:messageName])
{
id messageBody = message.body;
NSLog(@"%@",messageBody);
//弹出一个新的红色背景的控制器
UIViewController *newVC = [[UIViewController alloc]init];
newVC.view.backgroundColor = [UIColor redColor];
//添加一个返回按钮,返回webView
CGRect rect = CGRectMake(100,100,100,50);
UIButton *button = [[UIButton alloc]initWithFrame:rect];
[button setTitle:@"返回" forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize:20];
[button addTarget:self action:@selector(dismissNewVC) forControlEvents:UIControlEventTouchUpInside];
[newVC.view addSubview:button];
[self presentViewController:newVC animated:YES completion:nil];
}
}
//返回方法
-(void)dismissNewVC
{
[self dismissViewControllerAnimated:YES completion:nil];
}
//点击oc按钮,调用js方法
-(void)back
{
//第一种:直接调用
//无论web页面跳转多少次,只要按钮存在,js都可以生效
[self.webView evaluateJavaScript:@"function sayHello(){ \
alert('jack') \
} \
sayHello()"
completionHandler:^(id _Nullable result, NSError * _Nullable error) {
}];
//第二种:直接调用html文件中的js代码
//注意:这种方式只有在第一个web页面js才能生效,跳转到第二个web页面就无效了
//因为页面跳转后,就不是我们引入的本地的html页面了,自然也就引入不了我们本地的js代码
//不过也正常,我们一般只需要在第一个页面添加一个返回按钮,dismiss掉这个webView,其他的功能都可以用html的按钮实现
[self.webView evaluateJavaScript:@"hello()"completionHandler:^(id _Nullable result, NSError * _Nullable error) {
}];
//第三种:调用html文件中引入的js文件的js代码
//注意:js效果与第二种相同
[self.webView evaluateJavaScript:@"back()"completionHandler:^(id _Nullable result, NSError * _Nullable error) {
}];
}
//wkWebView直接调用js的弹窗是无效的,需要拦截js中的alert,用oc的方式展现出来
//该方法中的message参数就是我们JS代码中alert函数里面的参数内容
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
NSLog(@"----------%@",message);
UIAlertController *alertView = [UIAlertController alertControllerWithTitle:@"js的弹窗" message:message preferredStyle:UIAlertControllerStyleAlert];
[alertView addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
//一定要调用下这个block,否则会崩
//API说明:The completion handler to call after the alert panel has been dismissed
completionHandler();
}]];
[self presentViewController:alertView animated:YES completion:nil];
}
@end
WKDelegateController.h文件:
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
@class WKDelegateController;
@protocol WKDelegate <NSObject>
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
@end
@interface WKDelegateController : UIViewController <WKScriptMessageHandler>
@property (weak , nonatomic) id<WKDelegate> delegate;
@end
WKDelegateController.m文件
#import "WKDelegateController.h"
@interface WKDelegateController ()
@end
@implementation WKDelegateController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
if ([self.delegate respondsToSelector:@selector(userContentController:didReceiveScriptMessage:)])
{
[self.delegate userContentController:userContentController didReceiveScriptMessage:message];
}
}
index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>js与oc互调</title>
<link href="index.css" rel="stylesheet">
</head>
<body>
<button onclick="clickBtn()"> 点击按钮跳转到baidu </button>
<br>
<button onclick="dismiss()"> 点击按钮dismiss回去 </button>
<!--导航-->
<div >
<ul>
<div class="time" onclick="clickTime()">点击我present一个新的controller</div>
<li><a href="https://www.baidu.com" >baidu</a></li>
<li><a href="#" >about</a></li>
<li><a href="#" >services</a></li>
<li><a href="#" >gallery</a></li>
<li><a href="#" >contact</a></li>
</ul>
</div>
<!--引入的js函数文件-->
<script src="index.js" type="text/javascript"></script>
<!--直接写的js函数-->
<script type="text/javascript">
function hello(){
alert("你好!");
}
</script>
</body>
</html>
index.js文件
//确认js加载成功
window.onload = function (){
alert(0);
}
//点击html按钮,调用js方法
function clickBtn(){
window.location.href ="https://www.baidu.com";
}
//点击html按钮,调用oc方法
function dismiss(){
window.webkit.messageHandlers.ocMethod.postMessage("我点击html按钮,调用oc的方法,让webView消失");
}
//点击html的div标签,调用oc方法
function clickTime(){
window.webkit.messageHandlers.presentMethod.postMessage("我点击div标签,调用oc的方法,弹出一个控制器");
}
//点击oc按钮,要调用的js方法
function back() {
window.location.href = "https://www.baidu.com";
}
最后是无关紧要的index.css文件
*{
padding = 0;
margin = 0;
}
body{
padding:100px 0 0 0;
text-align:center;
background-color:lightgray;
font-size:40px;
}
button{
font-size:50px;
}
li{
list-style-type:none
}
a{
font-size:50px;
}
比较好的链接,也介绍了一些第三方框架:
这个实现网易新闻详情页写的很详细,所以我就不写了.但是注意这里遗漏了解决循环引用问题,练习的时候要注意:<a href="http://www.jianshu.com/p/75f3abd40cc1">iOS-使用WKWebview实现新闻详情页</a>
<a href="http://www.jianshu.com/p/1f2dc3d3090a">iOS WKWebView导致ViewController不调用dealloc方法</a>
WKWebView并不会去NSHTTPCookieStorage中读取cookie,因此导致cookie丢失解决办法:<a href="http://www.jianshu.com/p/4fa8c4eb1316">IOS进阶之WKWebView</a>
讲UIWebView与JavaScriptCore细的文章:
<a href="http://www.jianshu.com/p/d19689e0ed83">iOS下JS与原生OC互相调用(总结)</a>
<a href="http://liuyanwei.jumppo.com/2016/04/03/iOS-JavaScriptCore.html">iOS JavaScriptCore使用</a>
<a href="http://www.jianshu.com/p/84a6b1ac974a">iOS H5容器的一些探究(一):UIWebView和WKWebView的比较和选择</a>