iOS开发,js与原生OC交互

需求:APP开发中 帮助与反馈页面 有一个链接需要跳转到网页,但是需要点击网页上面的返回按钮返回APP中
解决方案:(小编亲测好使)
  1. 使用WKWebViewWKUserContentController
  2. 为OC与js交互注册通用交互方法js_obj,代码如下:
    [userContent addScriptMessageHandler:self name:@"js_obj"];
  3. WKScriptMessageHandler协议userContentController方法里面编写OC接受js回调信息,然后判断约定的字段是否一致,如果一致的话,就执行相应的代码,代码如下:
    // 判断是否是调用原生的
    if([@"js_obj" isEqualToString:message.name]) {
        if ([@"close" isEqualToString:message.body]) {
            [self back];
        }
    }
  1. 在代码最后,一定要把注册的东西移除掉
- (void)dealloc {
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"js_obj"];
}
  1. 前段所要做的工作只有:
    在点击按钮返回的时候调用
    window.webkit.messageHandlers.js_obj.postMessage("close") // js向OC发送返回消息
  2. 注意,可能会造成循环引用,页面销毁时不调用dealloc方法,所以要在返回方法里面注销一下,加入如下代码:
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"js_obj"];
  3. 全部代码如下:
#import "MineHelpViewController.h"
#import "UserManager.h"
#import <WebKit/WebKit.h>

@interface MineHelpViewController ()<WKScriptMessageHandler,WKUIDelegate,WKNavigationDelegate>

@property(nonatomic,strong)WKWebView *webView;
@property(nonatomic,weak)NSURLRequest *request;

@end

@implementation MineHelpViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    [self setupFromWebView];
}

// 导航栏返回
- (void)back {
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"js_obj"];
    [self.navigationController popViewControllerAnimated:YES];
}

- (void)setupFromWebView {
    // 1. 配置Configuration信息
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    config.selectionGranularity = WKSelectionGranularityDynamic;
    config.allowsInlineMediaPlayback = YES;
    WKPreferences *preferences = [WKPreferences new];
    // 是否支持JavaScript
    preferences.javaScriptEnabled = YES;
    // 不通过用户交互,是否可以打开窗口
    preferences.javaScriptCanOpenWindowsAutomatically = YES;
    config.preferences = preferences;
    // 创建UserContentController(提供JavaScript向webView发送消息的方法)
    // WKUserContentController 是JavaScript与原生进行交互的桥梁
    WKUserContentController *userContent = [[WKUserContentController alloc] init];
    // 添加消息处理,注意:self指代的对象需要遵守 WKScriptMessageHandler 协议,结束时需要移除
    [userContent addScriptMessageHandler:self name:@"js_obj"];
    // 将UserConttentController设置到配置文件
    config.userContentController = userContent;
    //
    self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:config];
    self.webView.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:self.webView];
    NSURL *url = [NSURL URLWithString:[self.webURL stringByAppendingFormat:@"?token=%@",[UserManager defaultManager].token]];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    // 设置内边距
    self.webView.navigationDelegate = self;
    self.webView.UIDelegate = self;
    [self.webView loadRequest:request];
}

#pragma mark - WKScriptMessageHandler
//  JS 端可通过 window.webkit.messageHandlers.js_obj.postMessage("123") 发送消息
/* JS 调用原生 */
- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message {
    // 判断是否是调用原生的
    if([@"js_obj" isEqualToString:message.name]) {
        if ([@"close" isEqualToString:message.body]) {
            [self back];
        }
    }
}

#pragma mark - WKNavigationDelegate

/* 页面开始加载 */
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
    NSLog(@"----页面开始加载");
}

/* 页面返回内容 */
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
    NSLog(@"----页面返回内容");
}

/* 页面加载完成 */
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    NSLog(@"----页面加载完成");
}


/* 页面加载失败 */
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    NSLog(@"----页面加载失败");
}

/* 在发送请求之前,决定是否跳转 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSURL *url =  navigationAction.request.URL;
    NSString *urlStr = url.absoluteString;
    NSLog(@"【load url】=== %@", urlStr);
    //不允许跳转
    //decisionHandler(WKNavigationActionPolicyCancel);
    //return;
    //允许跳转
    decisionHandler(WKNavigationActionPolicyAllow);
}

/* 在收到响应之后,决定是否跳转 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
    NSLog(@"===%@",navigationResponse.response.URL.absoluteString);
    //允许跳转
    decisionHandler(WKNavigationResponsePolicyAllow);
    //不允许跳转
    //decisionHandler(WKNavigationResponsePolicyCancel);
}

/* 接受到服务器跳转请求之后调用 */
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
    NSLog(@"----接收到服务器跳转请求之后调用");
}

/* 加载数据发生错误时调用  */
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    NSLog(@"----数据加载发生错误时调用");
}

/* 需要响应身份验证时调用,同样在block中需要传入用户身份凭证 */
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
    //用户身份信息
    NSLog(@"----需要响应身份验证时调用 同样在block中需要传入用户身份凭证");
    NSURLCredential *newCard = [NSURLCredential credentialWithUser:@"" password:@"" persistence:NSURLCredentialPersistenceNone];
    // 为 challenge 的发送方提供 credential
    [[challenge sender] useCredential:newCard forAuthenticationChallenge:challenge];
    completionHandler(NSURLSessionAuthChallengeUseCredential,newCard);
}

/* 进程被终止时调用 */
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
    NSLog(@"----进程被终止时调用");
}

#pragma mark - WKUIDelegate

// WKUIDelegate是web界面中有弹出警告框时调用这个代理方法,主要是用来处理使用系统的弹框来替换JS中的一些弹框的,比如: 警告框, 选择框, 输入框等

/**
 webView中弹出警告框时调用, 只能有一个按钮
 @param webView webView
 @param message 提示信息
 @param frame 可用于区分哪个窗口调用的
 @param completionHandler 警告框消失的时候调用, 回调给JS
 */
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    NSLog(@"----web界面中有弹出警告框时调用");
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"警告" message:message preferredStyle:(UIAlertControllerStyleAlert)];
    UIAlertAction *ok = [UIAlertAction actionWithTitle:@"我知道了" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }];
    [alert addAction:ok];
    [self presentViewController:alert animated:YES completion:nil];
}

// JavaScript调用confirm方法后回调的方法 confirm是js中的确定框,需要在block中把用户选择的情况传递进去

/** 对应js的confirm方法
 webView中弹出选择框时调用, 两个按钮
 @param webView webView description
 @param message 提示信息
 @param frame 可用于区分哪个窗口调用的
 @param completionHandler 确认框消失的时候调用, 回调给JS, 参数为选择结果: YES or NO
 */
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler {
    NSLog(@"%@",message);
    completionHandler(YES);
    /*
     UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"请选择" message:message preferredStyle:(UIAlertControllerStyleAlert)];
     UIAlertAction *ok = [UIAlertAction actionWithTitle:@"同意" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
     completionHandler(YES);
     }];
     UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"不同意" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
     completionHandler(NO);
     }];
     [alert addAction:ok];
     [alert addAction:cancel];
     [self presentViewController:alert animated:YES completion:nil];
     */
}

// JavaScript调用prompt方法后回调的方法 prompt是js中的输入框 需要在block中把用户输入的信息传入

/** 对应js的prompt方法
 webView中弹出输入框时调用, 两个按钮 和 一个输入框
 
 @param webView webView description
 @param prompt 提示信息
 @param defaultText 默认提示文本
 @param frame 可用于区分哪个窗口调用的
 @param completionHandler 输入框消失的时候调用, 回调给JS, 参数为输入的内容
 */
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler {
    NSLog(@"%@",prompt);
    completionHandler(@"123");
    /*
     UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"请输入" message:prompt preferredStyle:(UIAlertControllerStyleAlert)];
     [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
     textField.placeholder = @"请输入";
     }];
     UIAlertAction *ok = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
     UITextField *tf = [alert.textFields firstObject];
     completionHandler(tf.text);
     }];
     UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
     completionHandler(defaultText);
     }];
     [alert addAction:ok];
     [alert addAction:cancel];
     [self presentViewController:alert animated:YES completion:nil];
     */
}

/* 关闭webView时调用的方法 */
- (void)webViewDidClose:(WKWebView *)webView {
    NSLog(@"----关闭webView时调用的方法");
}

#pragma mark - dealloc

- (void)dealloc {
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"js_obj"];
}

写在最后:
WKWebView和UIWebView的区别:
主要是性能问题,UIWebView占用过多内存,而且WKWebView有诸多新特性
1.更多的支持HTML5的特性
2.官方宣称的高达60fps的滚动刷新率以及内置手势
3.与Safari相同的JavaScript引擎
4.将UIWebViewDelegate与UIWebView拆分成了14类与3个协议
5.增加加载进度属性:estimatedProgress

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

推荐阅读更多精彩内容