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 

@interface MineHelpViewController ()

@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

你可能感兴趣的:(iOS开发,js与原生OC交互)