1. 程式人生 > >OC與JS互動 -- 原生

OC與JS互動 -- 原生

本文只是介紹簡單的OC與JS互動

一、OC呼叫JS

<html>
    <header>
        <meta http-equiv="Content-Type" content="text/html"; charset="UTF-8"/>
        <title>zhaoName製作的網頁</title>
    </header>

    <script Type = "text/javascript">
        function login()
        {
            return
100; }
</script> <body> 電話:10086 <button style="background:red; width:120px; height:30px;">call</button> </body> </html>

呼叫webView的方法

// 獲取網頁的標題
self.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
// 呼叫JS中的函式
NSLog(@"%@", [webView stringByEvaluatingJavaScriptFromString:@"login();"]);

該方法返回一個字串,注意它是一個同步方法,若使用它執行非常耗時的操作會導致介面的卡頓。有兩個解決方案:
1>使用官方推薦webView方法evaluateJavaScript:completionHandler:
2>自定義一個延遲執行方法來防止阻塞,或將方法放到setTimeout 中

這裡寫圖片描述

二、JS呼叫OC

<html>
    <header>
        <meta http-equiv
="Content-Type" content="text/html"; charset="UTF-8"/>
<title>zhaoName製作的網頁</title> </header> <script Type = "text/javascript"> function login() { location.href = 'zhaoName://openLibrary'; } </script> <body> 電話:10086 <button style="background:red; width:120px; height:30px;" onclick="login();">call</button> <br> <a href="https://github.com/zhaoName">zhaoName的Git</a> </body> </html>

以下都是點選webView上的按鈕”call”,呼叫OC中的方法

1、沒有引數

/**
 * 通過這個方法完成JS調OC
 */
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSString *url = @"zhaoname://";

    if ([request.URL.absoluteString hasPrefix:url])
    {
        NSString *methodName = [request.URL.absoluteString substringFromIndex:url.length];
        [self performSelector:NSSelectorFromString(methodName) withObject:nil];
        return NO;
    }

    NSLog(@"想載入網路請求,不想做JS呼叫OC");
    return YES;
}

- (void)openLibrary
{
    NSLog(@"JS呼叫OC方法--%s", __func__);
}

這裡寫圖片描述

2、一個引數

上面HTML中函式修改為

function login()
{
    location.href = 'zhaoName://sendMessage_?How are you';
}

OC獲取的路徑path會自動刪除’:’,所以此處用’_’代替。引數用?分割。(實際開發中需要你和寫HTML的人共同制定JS調OC的規則)

/**
 * 通過這個方法完成JS掉OC
 */
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSString *url = @"zhaoname://";

    if ([request.URL.absoluteString hasPrefix:url])
    {
        // 路徑
        NSString *path = [request.URL.absoluteString substringFromIndex:url.length];
        NSArray *subPaths = [path componentsSeparatedByString:@"?"];
        // 獲取方法名
        NSString *methodName = [subPaths.firstObject stringByReplacingOccurrencesOfString:@"_" withString:@":"];
        // 引數
        NSString *para = subPaths.lastObject;
        [self performSelector:NSSelectorFromString(methodName) withObject:para];
        return NO;
    }

    NSLog(@"想載入網路請求,不想做JS呼叫OC");
    return YES;
}


- (void)sendMessage:(NSString *)message
{
    NSLog(@"JS呼叫OC方法--%s 引數:%@", __func__, message);
}

這裡寫圖片描述

3、兩個引數

上面HTML中函式修改為

function login()
{
     location.href = 'zhaoName://sendMessage_sendName_?hehe&zhaoName';
}

‘:’用’_’代替,方法名和引數用’?’分割,引數之間用’&’分割

/**
 * 通過這個方法完成JS掉OC
 */
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSString *url = @"zhaoname://";

    if ([request.URL.absoluteString hasPrefix:url])
    {
        // 路徑
        NSString *path = [request.URL.absoluteString substringFromIndex:url.length];
        NSArray *subPaths = [path componentsSeparatedByString:@"?"];
        // 獲取方法名
        NSString *methodName = [subPaths.firstObject stringByReplacingOccurrencesOfString:@"_" withString:@":"];
        // 引數
        NSArray *paras = nil;
        // 防止出現無引數的情況
        if(subPaths.count == 2 || [subPaths.lastObject containsString:@"&"])
        {
             paras = [subPaths.lastObject componentsSeparatedByString:@"&"];

        }
        // 防止出現引數為空的情況
        NSString *firstPara = paras.firstObject;
        NSString *secondPara = paras.count <= 1 ? nil : paras.lastObject;
        [self performSelector:NSSelectorFromString(methodName) withObject:firstPara withObject:secondPara];

        return NO;
    }

    NSLog(@"想載入網路請求,不想做JS呼叫OC");
    return YES;
}


- (void)sendMessage:(NSString *)message sendName:(NSString *)sendName
{
    NSLog(@"JS呼叫OC方法--%s 引數:%@ %@", __func__, message, sendName);
}

這裡寫圖片描述

4、三個及以上的引數

系統自帶的方法performSelector最多隻能傳兩個引數,所以有三個及以上的引數我們要藉助NSInvocation類。

NSObject+Invocation.h檔案

#import <Foundation/Foundation.h>

@interface NSObject (Invocation)


/**
 * 基於NSObject封裝一個可用於JS呼叫OC方法的分類,適用於引數可有可無,可有多個

 @param selector 方法名
 @param parameters 引數
 @return 返回值
 */
- (id)performSelector:(SEL)selector withParameters:(NSArray *)parameters;

@end

NSObject+Invocation.m檔案

#import "NSObject+Invocation.h"

@implementation NSObject (Invocation)

- (id)performSelector:(SEL)selector withParameters:(NSArray *)parameters
{
    // 方法簽名
    NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:selector];
    if(signature == nil) return nil;
    // 利用NSIvocation可以包裝一次方法呼叫(設定方法呼叫者、方法名、引數、返回值)
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];

    // 設定方法呼叫者
    invocation.target = self;
    // 設定方法名
    invocation.selector = selector;
    // 設定方法引數 注意引數下標從2開始,0、1被系統佔用
    for(int i=0; i<parameters.count; i++)
    {
        id object = parameters[i];
        if([object isKindOfClass:[NSNull class]]) continue;
        [invocation setArgument:&object atIndex:i+2];
    }

    // 執行
    [invocation invoke];

    // 設定返回值
    id returnValue = nil;
    // 兼顧沒有返回值的方法
    if(signature.methodReturnLength != 0)
    {
        [invocation getReturnValue:&returnValue];// 說明此方法有返回值
    }
    return returnValue;
}

@end

上面HTML中函式修改為

function login()
{
    location.href = 'zhaoName://sendMessage_sendName_reviceName_?hehe&zhaoName&nicai';
}
/**
 * 通過這個方法完成JS掉OC
 */
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSString *url = @"zhaoname://";

    if ([request.URL.absoluteString hasPrefix:url])
    {
        // 路徑
        NSString *path = [request.URL.absoluteString substringFromIndex:url.length];
        NSArray *subPaths = [path componentsSeparatedByString:@"?"];
        // 獲取方法名
        NSString *methodName = [subPaths.firstObject stringByReplacingOccurrencesOfString:@"_" withString:@":"];
        // 引數
        NSArray *paras = [subPaths.lastObject componentsSeparatedByString:@"&"];
        // 呼叫OC中的方法 並獲取返回值
        NSString *result = [self performSelector:NSSelectorFromString(methodName) withParameters:paras];
        NSLog(@"%@", result);
        return NO;
    }

    NSLog(@"想載入網路請求,不想做JS呼叫OC");
    return YES;
}


- (NSString *)sendMessage:(NSString *)message sendName:(NSString *)sendName reviceName:(NSString *)reviceName
{
    NSLog(@"JS呼叫OC方法--%s 引數:%@ %@ %@", __func__, message, sendName, reviceName);
    return @"發信息";
}

這裡寫圖片描述