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的控制器
点击获取源代码

效果图就不放了,创建几个文件把代码全部复制一下就可以看到效果了

iOS与HTML混编(端内JS与OC交互)基础_第1张图片
我是文件列表

ViewController中有个按钮,点击即可跳转到WebVC.
WebVC的.m文件,其中值得注意的是WKDelegate,是解决WKWebView循环引用问题的代理

#import "WebVC.h"
#import 
#import "WKDelegateController.h"

@interface WebVC ()

@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 
#import 
@class WKDelegateController;

@protocol WKDelegate 

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

@end

@interface WKDelegateController : UIViewController 

@property (weak , nonatomic) id 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文件



    
        
            js与oc互调
            
    
    
        
        

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;
}

比较好的链接,也介绍了一些第三方框架:
这个实现网易新闻详情页写的很详细,所以我就不写了.但是注意这里遗漏了解决循环引用问题,练习的时候要注意:iOS-使用WKWebview实现新闻详情页

iOS WKWebView导致ViewController不调用dealloc方法

WKWebView并不会去NSHTTPCookieStorage中读取cookie,因此导致cookie丢失解决办法:IOS进阶之WKWebView

讲UIWebView与JavaScriptCore细的文章:
iOS下JS与原生OC互相调用(总结)
iOS JavaScriptCore使用
iOS H5容器的一些探究(一):UIWebView和WKWebView的比较和选择

你可能感兴趣的:(iOS与HTML混编(端内JS与OC交互)基础)