UIWebView-Objective-C 與 JavaScript交互

如果項目中設(shè)計Html頁面的,都少不了Objective-C與JavaScript的交互,如果你的項目比較新,那應(yīng)該是Swift與JavaScript交互,差別不大.交互有兩個形式,一種是Html頁面JavaScript對方或方法調(diào)用Objective-C代碼,另外一種是Objective-C調(diào)用JavaScript代碼.

JavaScript 調(diào)用 Objective-C

JavaScript對方或方法調(diào)用Objective-C代碼有三種方式:
1.URL攔截,通過自定義的scheme實現(xiàn).
2.JavaScriptCore新增或替換JavaScript方法.
3.通過實現(xiàn)JSExport協(xié)議生成對象,實現(xiàn)JavaScript對象調(diào)用OC對象方法.

第一種方式比較常見,實現(xiàn)UIWebViewDelegate的可選方法:

__TVOS_PROHIBITED @protocol UIWebViewDelegate <NSObject>

@optional
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
- (void)webViewDidStartLoad:(UIWebView *)webView;
- (void)webViewDidFinishLoad:(UIWebView *)webView;
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;

@end

以訪問百度網(wǎng)頁為例:

#pragma mark - UIWebviewDelegate

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    
    NSString *requestStr = [request.URL absoluteString];
    NSLog(@"網(wǎng)絡(luò)請求地址:%@",requestStr);
    if ( [[requestStr lowercaseString] containsString:@"news"]) {
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"FlyElephant---新聞" message:@"" preferredStyle:(UIAlertControllerStyleAlert)];
        UIAlertAction *sureAlertAction = [UIAlertAction actionWithTitle:@"OK" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
            
        }];
        [alert addAction:sureAlertAction];
        [self presentViewController:alert animated:YES completion:^{
            
        }];
        return NO;
    }
    
    return YES;
}

- (void)webViewDidStartLoad:(UIWebView *)webView {
    NSLog(@"開始---webViewDidStartLoad");
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    NSLog(@"加載結(jié)束---webViewDidFinishLoad");
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
    NSLog(@"加載出錯---didFailLoadWithError");
}

- (void)setUp {
    UIWebView *webView = [[UIWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    webView.delegate = self;
    NSURL *url = [NSURL URLWithString:@"https://www.baidu.com/"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [webView loadRequest:request];
    [self.view addSubview:webView];
}
新聞.png

2.第二種方式需要一個簡單的Html頁面配合,代碼如下:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
            <title>JavaScript頁面</title>
            <script type="text/javascript">
                function jsClick2() {
                    alert('FlyElephant---本地的實現(xiàn)');
                }
            </script>
    </head>
    <body>
        <p>測試</p>
         <button onclick="jsClick()">測試按鈕</button>
        <button onclick="jsClick2()">測試按鈕2</button>
    </body>
</html>

導(dǎo)入JavaScriptCore頭文件之后,在代理中設(shè)置JSContext.

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    NSLog(@"加載結(jié)束---webViewDidFinishLoad");
    self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    [self setUpJavaScriptCallBack];
}

第一個按鈕點擊時候JavaScript沒有對應(yīng)的方法實現(xiàn),第二個Objective-C對響應(yīng)方法進行了重寫:

    __weak typeof(self) weakSelf = self;
    self.jsContext[@"jsClick"] = ^ {
        NSArray *args = [JSContext currentArguments];
        NSMutableArray *messages = [NSMutableArray array];
        for (JSValue *obj in args) {
            [messages addObject:[obj toObject]];
        }
        [weakSelf.jsContext evaluateScript:@"alert('FlyElephant---本地的實現(xiàn)')"];
    };
    
    self.jsContext[@"jsClick2"] = ^ {
        [weakSelf.jsContext evaluateScript:@"alert('替換原有jsClick2方法')"];
    };
交互.png

3.JSContext并不能實現(xiàn)OC對象和JS對象之間的直接轉(zhuǎn)換,兩者面向?qū)ο蟮脑O(shè)計方式是不一樣的,OC是基于類繼承的,JS是基于原型的,但是所有的對象都可以視為鍵值對集合.

JavaScript可以脫離原型直接用JSON表示對象,但是OC不能脫離繼承來表示對象,JavaScriptCore提供了JSExport作為兩種語言的互通協(xié)議.

JSExport沒有定義任何可選方法,需要首先定義一個實現(xiàn)JSExport的協(xié)議,然后在自定義協(xié)議中定義JavaScript調(diào)用方法,有點繞,看代碼吧.

@protocol UserExportProtocol <JSExport>

- (void)buy:(NSString *)name;

@end


@interface User : NSObject<UserExportProtocol>

@end
@implementation User

- (void)buy:(NSString *)name {
    NSLog(@"FlyElephant買了---%@",name);
}

@end

OC端調(diào)用:

    self.jsContext[@"user"] = tempUser;
    NSString *buyStr = @"user.buy('書籍')";
    [self.jsContext evaluateScript:buyStr];

打印的Log信息如下:
FlyElephant買了---書籍
可以通過對已有的類添加JSExport協(xié)議,定義一個UIButton的擴展協(xié)議.

@protocol UIButtonExportProtocol <JSExport>

- (void)setTitle:(NSString *)title forState:(UIControlState)state;

@end
    
    self.jsContext[@"button"] = self.testButton;
    [self.jsContext evaluateScript:@"button.setTitleForState('FlyElephant 改變', 0)"];
按鈕.png

Objective-C 調(diào)用 JavaScript

Objective-C調(diào)用JavaScript的方式有兩種:
1.stringByEvaluatingJavaScriptFromString
2.evaluateScript

第一種方式是webView中的方法不能確定調(diào)用之后是否出錯,調(diào)用的結(jié)果都是以字符串的形式方式,比較局限.

self.navigationItem.title = [self.webView stringByEvaluatingJavaScriptFromString:@"document.title"];

第二種方式比較簡單,需要先獲取JSContext:

self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    JSValue *value = [self.jsContext evaluateScript:@"document.title"];
    self.navigationItem.title = value.toString;
    
    [self.jsContext setExceptionHandler:^(JSContext *context, JSValue *exception){
        NSLog(@"%@", exception);
    }];

如果你覺得以上方式比較復(fù)雜,那么請選用WebViewJavascriptBridge吧,接下來會對WebViewJavascriptBridge進行源碼分析,歡迎關(guān)注.

參考資料:

iOS7新JavaScriptCore框架入門介紹
JavaScriptCore框架在iOS7中的對象交互和管理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容