UIWebView和JS交互詳解

上個項目用到UIWebView和JS的交互,這里總結一下兩者之間交互的方法歧沪。
以下代碼都在這個demo里骤铃。
先來研究下JS調用OC的方法

攔截假的URL

比如html上有一個按鈕,想要點擊按鈕調用OC的方法礁苗。
在JS中點擊按鈕后用JS發(fā)起一個假的URL請求弟灼,然后利用webView的代理方法shouldStartLoadWithRequest來拿到這個假的URL字符串,這個字符串包含了需要調用的方法名和傳入的參數冒黑。下面是HTML的代碼

<!DOCTYPE html>
<html>
    <head lang="en">
        <meta charset="UTF-8">
            <title></title>
    </head>
    <body>
        <p></p>
        <div>
            <button onclick="showToast();">無參數彈框提示</button>
            <button onclick="showToastWithParameter();">有參數彈框提示</button>
            <button onclick="shareClick();">Click Me!</button>

        </div>
        <p></p>
        <script>
        function showToast() {
            window.location.href = 'test1://showToast';
        }
        
        function showToastWithParameter() {
            window.location.href = 'test1://showToastWithParameter?parama=666666';
        }
        
        
        //不使用window.location.href
        function loadURL(url) {
            var iFrame;
            iFrame = document.createElement("iframe");
            iFrame.setAttribute("src", url);
            iFrame.setAttribute("style", "display:none;");
            iFrame.setAttribute("height", "0px");
            iFrame.setAttribute("width", "0px");
            iFrame.setAttribute("frameborder", "0");
            document.body.appendChild(iFrame);
            // 發(fā)起請求后這個 iFrame 就沒用了田绑,所以把它從 dom 上移除掉
            iFrame.parentNode.removeChild(iFrame);
            iFrame = null;
        }
        function shareClick() {
            loadURL("Test2://shareClick?title=分享的標題&content=分享的內容&url=鏈接地址&imagePath=圖片地址");
        }
        </script>
    </body>
</html>

OC代碼:

- (void)viewDidLoad {
    [super viewDidLoad];
    _webView = [[UIWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    _webView.delegate = self;
    [self.view addSubview:_webView];
    NSURL *htmlURL = [[NSBundle mainBundle] URLForResource:@"File.html" withExtension:nil];
    NSURLRequest *request = [NSURLRequest requestWithURL:htmlURL];
    [_webView loadRequest:request];
    
    
    // Do any additional setup after loading the view.
}

#pragma mark -- UIWebViewDelegate

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    NSString *scheme = request.URL.scheme;
    NSString *host = request.URL.host;
    NSString *query = request.URL.query;
    NSString * url = request.URL.absoluteString;
    if ([scheme isEqualToString:@"test1"]) {
        NSString *methodName = host;
        if (query) {
            methodName = [methodName stringByAppendingString:@":"];
        }
        SEL sel = NSSelectorFromString(methodName);
        NSString *parameter = [[query componentsSeparatedByString:@"="] lastObject];
        [self performSelector:sel withObject:parameter];
        return NO;
    }else if ([scheme isEqualToString:@"test2"]){//JS中的是Test2,在攔截到的url scheme全都被轉化為小寫。
        NSURL *url = request.URL;
        NSArray *params =[url.query componentsSeparatedByString:@"&"];
        NSMutableDictionary *tempDic = [NSMutableDictionary dictionary];
        for (NSString *paramStr in params) {
            NSArray *dicArray = [paramStr componentsSeparatedByString:@"="];
            if (dicArray.count > 1) {
                NSString *decodeValue = [dicArray[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
                [tempDic setObject:decodeValue forKey:dicArray[0]];
            }
        }
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"方式一" message:@"這是OC原生的彈出窗" delegate:self cancelButtonTitle:@"收到" otherButtonTitles:nil];
        [alertView show];
        NSLog(@"tempDic:%@",tempDic);
        return NO;
    }
    return YES;
}


- (void)webViewDidStartLoad:(UIWebView *)webView{
    
}

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
    
}

- (void)showToast {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:@"JS調用OC代碼成功抡爹!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
    [alert show];
}

- (void)showToastWithParameter:(NSString *)parama {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:[NSString stringWithFormat:@"JS調用OC代碼成功 - JS參數:%@",parama] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
    [alert show];
}

需要注意的是,在UIWebView的代理方法中攔截到的scheme會轉換成小寫

點擊模擬器上的第一個和第二個按鈕分別會調用本地的showToast和showToastWithParameter:方法。第三個按鈕中沒有使用
window.location.href,在別的文章中看到了下面的解釋:

因為如果當前網頁正使用window.location.href加載網頁的同時超埋,調用window.location.href去調用OC原生方法卦溢,會導致加載網頁的操作被取消掉。
同樣的泵殴,如果連續(xù)使用window.location.href執(zhí)行兩次OC原生調用涮帘,也有可能導致第一次的操作被取消掉。所以我們使用自定義的loadURL笑诅,來避免這個問題调缨。loadURL的實現來自關于UIWebView和PhoneGap的總結一文疮鲫。

使用JavaScriptCore

iOS7之后,app新添加了一個JavaScriptCore/JavaScriptCore.h這個庫用來做JS交互弦叶。注意俊犯,使用這種方式調用到OC的方法是在子線程中的。使用JavaScriptCore/JavaScriptCore.h做js調用oc有兩種方式伤哺,一種是用block注入燕侠,一種是使用JSExport協(xié)議來實現。
下面是h5的代碼:

<!DOCTYPE html>
<html>
    <head lang="en">
        <meta charset="UTF-8">
            <title></title>
    </head>
    <body>
        <p></p>
        <div>
            <button onclick="showToast();">彈框提示</button>
            <button onclick="JSObjective.showAlert('參數1','參數2');">alert提示</button>
        </div>
        <p></p>
        <script>
            function showToast() {
                // 調用OC中的showToastWithparams方法
                showToastWithparams("參數1","參數2","參數3");
            }
            </script>
    </body>
</html>

在HTML中立莉,添加一個事件有兩種方法绢彤,一種是<button onclick="showToast();">彈框提示</button>,另一種是<button onclick="JSObjective.showAlert('參數1','參數2');">alert提示</button>;對于第一種寫法,對應的OC代碼如下:

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    context[@"showToastWithparams"] = ^() {
        //NSLog(@"當前線程 - %@",[NSThread currentThread]);
        NSArray *params = [JSContext currentArguments];
        for (JSValue *Param in params) {
            NSLog(@"%@", Param); // 打印結果就是JS傳遞過來的參數
        }
        //注意桃序,這時候是在子線程中的杖虾,要更新UI的話要回到主線程中
        dispatch_async(dispatch_get_main_queue(), ^{
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:[NSString stringWithFormat:@"js調用oc原生代碼成功!"] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
            [alert show];
        });
    };    
}

對于第二種寫法,首先要定義一個繼承自JSExport的協(xié)議媒熊,協(xié)議里面定義的方法即是供JS調用的OC原生方法

@protocol TestJSExport<JSExport>
JSExportAs
(showAlert,
 - (void)showAlertWithParameters:(NSString *)parameterone parametertwo:(NSString *)parametertwo
 );
@end
- (void)webViewDidFinishLoad:(UIWebView *)webView{
    JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    // 用TestJSExport協(xié)議來關聯關聯JCObjective的方法
    context[@"JSObjective"] = self;
}
#pragma mark -- TestJSExport協(xié)議

- (void)showAlertWithParameters:(NSString *)parameterone parametertwo:(NSString *)parametertwo {
    NSLog(@"當前線程 - %@",[NSThread currentThread]);// 子線程
    //NSLog(@"JS和OC交互 - %@ -- %@",parameterone,parametertwo);
   //注意奇适,這個代理方法也是在子線程中的
    dispatch_async(dispatch_get_main_queue(), ^{
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:[NSString stringWithFormat:@"js調用oc原生代碼成功! - JS參數:%@,%@",parameterone,parametertwo] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
        [alert show];
    });
}

WebViewJavascriptBridge

WebViewJavascriptBridge
是一個第三方的庫用于和js交互的。它在做UIWebView的js調用oc方法的時候使用的是攔截假的url的方式芦鳍。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末嚷往,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子柠衅,更是在濱河造成了極大的恐慌皮仁,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件菲宴,死亡現場離奇詭異贷祈,居然都是意外死亡,警方通過查閱死者的電腦和手機喝峦,發(fā)現死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門势誊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谣蠢,你說我怎么就攤上這事粟耻。” “怎么了眉踱?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵挤忙,是天一觀的道長。 經常有香客問我谈喳,道長册烈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任叁执,我火速辦了婚禮茄厘,結果婚禮上矮冬,老公的妹妹穿的比我還像新娘。我一直安慰自己次哈,他們只是感情好胎署,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著窑滞,像睡著了一般琼牧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上哀卫,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天巨坊,我揣著相機與錄音,去河邊找鬼此改。 笑死趾撵,一個胖子當著我的面吹牛,可吹牛的內容都是我干的共啃。 我是一名探鬼主播占调,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼移剪!你這毒婦竟也來了究珊?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤纵苛,失蹤者是張志新(化名)和其女友劉穎剿涮,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體攻人,經...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡取试,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了怀吻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片想括。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖烙博,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情烟逊,我是刑警寧澤渣窜,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站宪躯,受9級特大地震影響乔宿,放射性物質發(fā)生泄漏。R本人自食惡果不足惜访雪,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一详瑞、第九天 我趴在偏房一處隱蔽的房頂上張望掂林。 院中可真熱鬧,春花似錦坝橡、人聲如沸泻帮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锣杂。三九已至,卻和暖如春番宁,著一層夾襖步出監(jiān)牢的瞬間元莫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工蝶押, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留踱蠢,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓棋电,卻偏偏與公主長得像茎截,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子离陶,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,178評論 25 707
  • 一、簡介 近兩年隨著HTML5的迅速發(fā)展與日趨成熟沉眶,越來越多的移動開發(fā)者選擇使用HTML5來進行混合開發(fā)打却,不僅節(jié)約...
    RainyGY閱讀 1,872評論 1 12
  • 發(fā)現 關注 消息 iOS 第三方庫、插件谎倔、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,107評論 4 62
  • 等待是一種借口柳击,也是阻擋我們夢想的腳步。 我們總是在學習時遇到困難片习,等別人做完好告訴我答案捌肴,或者等老師講解...
    姚庚泰閱讀 453評論 0 0
  • 太陽出來了 我一直覺得 陽光是最美的詞之一 充滿希望 始終樂觀積極 給予人力量
    Leslier閱讀 187評論 0 0