OC 与 JS 交互 (UIWebView & WKWebView)

本文对比分析了iOS8以后新推出的WKWebView与UIWebView在与JavaScript交互上的区别。介绍了如何在OC中实现jsCallNativeDoSomethingWithParams()和nativeCallJSSendParams()的方法,并探讨了WKWebView的一个bug,即首次加载URL时无法通过POST传递参数的问题,建议通过原生调用JS方法来解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

iOS8以后,苹果推出了新框架Webkit,提供了替换UIWebView的组件WKWebView。WKWebView 的优势不必多说,这里将两者与 JS 的交互分别做对比.

首先,和前端以及安卓同学定义好一样的方法:
js 调用原生: jsCallNativeDoSomethingWithParams()
原生调用js: nativeCallJSSendParams()

UIWebView 与 JS 的交互
引入库

#import <JavaScriptCore/JavaScriptCore.h>

加载 html

    NSURL *url = [NSURL URLWithString: @"www.baidu.com"];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL: url];
     //如果需要在加载请求的时候用 post 请求传参数的话,加上下面三句.WKWebView这个方式不行,下面有解决方法
     NSString *body = [NSString stringWithFormat: @"arg1=%@&arg2=%@", @"val1",@"val2"];
    [request setHTTPMethod: @"POST"];
    [request setHTTPBody: [body dataUsingEncoding: NSUTF8StringEncoding]];

    [self.webView loadRequest: request];

js 调用原生的两种方式:

第一种: 采用协议方法
添加协议并遵守

@protocol TestJSExport <JSExport>
- (void)jsCallNativeDoSomethingWithParams:(NSString *) jsonStr;
@end

@interface JSCallOCViewController : UIViewController<UIWebViewDelegate,TestJSExport>

@end

然后再把方法进行关联

#pragma mark - UIWebViewDelegate
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    // 打印异常
    self.context.exceptionHandler =
    ^(JSContext *context, JSValue *exceptionValue)
    {
        context.exception = exceptionValue;
        NSLog(@"异常 %@", exceptionValue);
    };

    // 以 JSExport 协议关联 native 的方法
    self.context[@"native"] = self;
}

#pragma mark - JSExport Methods

- (void)jsCallNativeDoSomethingWithParams:(NSString *) jsonStr
{
    NSLog(@"%@", jsonStr);
}

js 中的相关代码

 <input type="button" value="call native" onclick="native.jsCallNativeDoSomethingWithParams(input.value);" />
            <br/>

第二种,以 block 形式关联

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
     self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    // 打印异常
    self.context.exceptionHandler =
    ^(JSContext *context, JSValue *exceptionValue)
    {
        context.exception = exceptionValue;
        NSLog(@"异常 %@", exceptionValue);
    };
    self.context[@"jsCallNativeDoSomethingWithParams"] =
    ^(NSString *jsonStr)
    {
        NSLog(@"%@", jsonStr);
    };

}

js 相关代码

  <input type="button" value="调用原生" onclick="jsCallNativeDoSomethingWithParams('json');" />

原生调用js

- (void) nativeCallJSSendParams:(NSString *)jsonStr {
    NSNumber *inputNumber = [NSNumber numberWithInteger:[self.textField.text integerValue]];
    JSValue *function = [self.context objectForKeyedSubscript:@"nativeCallJSSendParams"];
    JSValue *result = [function callWithArguments:jsonStr];
    NSString * jsonStr = [NSString stringWithFormat:@"%@", [result toString]];
}

WKWebView 与 JS 的交互

注: WKWebView 有一个bug ,就是第一次在加载 url 的时候是无法使用 post 传参数的,因为请求过程中,请求体会丢失,所以,如果实在需要传值,可以再加载好之后,用原生调用 js 方法传值(下面会有介绍),参考:
https://stackoverflow.com/questions/26253133/cant-set-headers-on-my-wkwebview-post-request

引入库

#import <WebKit/WebKit.h>

继承代理

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

正常加载页面

 - (void)viewDidLoad
{
    [super viewDidLoad];

    [self.view addSubview:self.newsWebView];

    NSURL *url = [NSURL URLWithString: @"你的url"];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL: url];
    [self.newsWebView loadRequest: request];
}


#pragma mark - WKScriptMessageHandler

//js 调用 原生
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {

    NSLog(@"jsCallNativeMessageBody ==> %@", message.body);

    if ([message.name isEqualToString:@"jsCallNativeDoSomethingWithParams"]) {
        [self jsCallNativeDoSomethingWithParams:message.body];
    }
}

//原生 调用 js
- (void) nativeCallJSSendParams:(NSString *)jsonStr {
    //注意:这里得把json字符串去掉空格,回车才可以,不然会报错
    NSString * paramStr = [self noWhiteSpaceString:jsonStr];
    NSString *returnJSStr = [NSString stringWithFormat:@"nativeCallJSSendUserMessage('%@')",paramStr];

    [self.newsWebView evaluateJavaScript:returnJSStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        NSLog(@"nativeCallJSSendParams result = %@,error = %@", result, error);
    }];
}

- (NSString *)noWhiteSpaceString: (NSString *)newString {
    newString = [newString stringByReplacingOccurrencesOfString:@"\r" withString:@""];
    newString = [newString stringByReplacingOccurrencesOfString:@"\n" withString:@""];
    newString = [newString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];      
    newString = [newString stringByReplacingOccurrencesOfString:@" " withString:@""];
      return newString;
}


#pragma mark - getter
- (WKWebView *)newsWebView {
    if (!_newsWebView) {
        WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
        configuration.userContentController = [[WKUserContentController alloc] init];
        [configuration.userContentController addScriptMessageHandler:self name:@"jsCallNativeDoSomethingWithParams"];
        _newsWebView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
        _newsWebView.UIDelegate = self;
        _newsWebView.navigationDelegate = self;
    }
    return _newsWebView;
}

另外告知前端同学(如果需要的话)添加对应 js 的方式:

    function showMessageFromWKWebViewClick() {            
        window.webkit.messageHandler.nativeCallJSSendParams.postMessage('js调用原生'); 
    }

    function nativeCallJSSendParams(returnStr) {

        document.getElementById("oneEle").value = returnStr;
                    alert(returnStr);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值