iOS与HTML混编(端内JS与OC交互)基础

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>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,019评论 4 62
  • 前言 关于UIWebView的介绍,相信看过上文的小伙伴们,已经大概清楚了吧,如果有问题,欢迎提问。 本文是本系列...
    Dark_Angel阅读 28,783评论 67 291
  • 前言 iOS开发中,用来显示一个html页、H5页,经常会用的一个控件是WebView。说到WebView,你知道...
    Dark_Angel阅读 23,384评论 31 287
  • 最近一段时间断断续续地下着雨,再者项目难以腾出时间,如此便找到了暂停跑步的借口,而全然不顾自己的懒惰。可是一段时间...
    叔丙仄阅读 410评论 1 5
  • 夜深人静,百无聊赖,偶翻某社交软件,看看他们生活的世界。 慢慢地好多事情都变得与己无关,渐渐地从生活的积极分子沦落...
    丏莫阅读 199评论 0 0