WebView和JS交互

前言

在開發(fā)IOS應(yīng)用的過程中,難免的會(huì)遇到和WebView打交道的場景,通常為了實(shí)現(xiàn)產(chǎn)品經(jīng)理的功能需求還要去和WebView里面的JS進(jìn)行交互。作為一個(gè)剛IOS開發(fā)的新人來說,第一次肯定會(huì)遇到各種問題和各種坑。在這里我把我的問題和解決方法羅列出來,希望對其他的大胸弟們有所幫助。

概述

IOS提供給我們的WebView控件類型總共有3中,UIWebView,WkWebView,SFSafariView。這三種類型的控件有這不同的使用限制和要求,在實(shí)際項(xiàng)目中,我們需要根據(jù)項(xiàng)目的實(shí)際要求選擇其中的一個(gè)就可以了。接下來,我會(huì)介紹一下這三種類型的區(qū)別。

UIWebView

UIWebView作為IOS應(yīng)用提供給開發(fā)者最早的一個(gè)控件,最初還是一個(gè)很不錯(cuò)的控件,但是基于今天的現(xiàn)狀而言,卻顯得有些雞肋了。加載速度慢,內(nèi)存開銷大對于產(chǎn)品和開發(fā)者來說都是很大的問題,特別是在第一次加載網(wǎng)頁時(shí),經(jīng)常會(huì)出現(xiàn)加載10秒以上的情況。

UIWebView加載過程

UIWebView加載網(wǎng)頁主要會(huì)用到一下四個(gè)方法

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

當(dāng)WebView收到一個(gè)打開鏈接請求時(shí),會(huì)觸發(fā)此方法,向我們詢問是否要加載這個(gè)鏈接請求,返回YES就表示允許加載這個(gè)鏈接請求,否則不加載鏈接請求。

- (void) webViewDidStartLoad:(UIWebView *)webView

當(dāng)上一步允許加載鏈接請求后,WebView會(huì)觸發(fā)此方法,告知我們要開始加載了,這時(shí)我們可以加一個(gè)loading的效果提示用戶。

- (void) webViewDidFinishLoad:(UIWebView *)webView

當(dāng)整個(gè)網(wǎng)頁加載完成之后會(huì)觸發(fā)此方法,如果上一步加上loading效果的話,在這個(gè)就要去掉了

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

當(dāng)加載過程中出錯(cuò),比如無網(wǎng)絡(luò)、404什么的,會(huì)執(zhí)行這個(gè)方法。這里我們要把loading去掉,如果為了友好的話,可以展示一個(gè)加載失敗的界面。

UIWebView和JS交互

UIWebView和JS交互主要依托于一個(gè)JSExport的協(xié)議,具體來說就是在加載網(wǎng)頁的過程中,我們?nèi)ツ玫骄W(wǎng)頁運(yùn)行JS的環(huán)境,然后JS執(zhí)行某些方法時(shí)我們就可以捕獲到,然后執(zhí)行自己的邏輯。

注意:和JS的交互過程中需要分兩種情況,一種是捕獲JS中無參數(shù)或一種參數(shù)的方法,另一種是捕獲JS中大于等于2個(gè)參數(shù)的方法。因?yàn)镺C中的方法名是由傳統(tǒng)意義上的方法名+外部參數(shù)名構(gòu)成的,所以當(dāng)我們捕獲第二種情況的JS方法時(shí)需要注意和OC方法名的對應(yīng)關(guān)系。下面的示例中會(huì)有介紹。

OK,接下來給大家放出一段示例代碼,示例中會(huì)講解到UIWebView和加載一個(gè)網(wǎng)頁的流程,以及網(wǎng)頁的JS如何和原生的代碼交互。

ViewController.h

//
//  ViewController.h
//  webviewDemo
//
//  Created by 孫天文 on 16/11/15.
//  Copyright ? 2016年 孫天文. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <JavaScriptCore/JavaScriptCore.h>

//定義一個(gè)和JS交互的協(xié)議,用來處理JS中的方法,這個(gè)協(xié)議中列出了4個(gè)方法,分別是無參數(shù)、一個(gè)參數(shù)、兩個(gè)參數(shù)以及三個(gè)參數(shù)的方法
@protocol demoJsResponseProtocol <JSExport>

- (void) sayHello;

- (void) setTitle:(NSString *)title;

//下面是大于等于兩個(gè)參數(shù)的情況,要注意這個(gè)格式和JS中的格式對比
- (void) setTitle:(NSString *)title Left:(NSString *)leftTitle;

- (void) setTitle:(NSString *)title Left:(NSString *)leftTitle Right:(NSString *)rightTitle;

@end

@interface ViewController : UIViewController<demoJsResponseProtocol>

@end

ViewController.m

//
//  ViewController.m
//  webviewDemo
//
//  Created by 孫天文 on 16/11/15.
//  Copyright ? 2016年 孫天文. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()<UIWebViewDelegate,JSExport>
    
@property (strong,nonatomic) UIWebView *webView;
 
 //JS運(yùn)行環(huán)境   
@property (strong,nonatomic) JSContext *jsContext;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 64, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
    self.webView.delegate = self;
    
    //加載本地html文件
    NSString *path = [[NSBundle mainBundle] pathForResource:@"test" ofType:@"html"];
    NSString *htmlString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
    NSString *basePath = [[NSBundle mainBundle] bundlePath];
    NSURL *baseURL = [NSURL fileURLWithPath:basePath];
    
    [self.webView loadHTMLString:htmlString baseURL:baseURL];
    
    [self.view addSubview:self.webView];
}
    
//網(wǎng)頁加載前調(diào)用的方法
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    
    return YES;
}
    
//開始加載
- (void) webViewDidStartLoad:(UIWebView *)webView{
    NSLog(@"start laod");
}
    
//加載完成
- (void) webViewDidFinishLoad:(UIWebView *)webView{
    self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    self.jsContext[@"demoapi"] = self;
    NSLog(@"laod finish");
}

//加載出錯(cuò)
- (void) webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
    NSLog(@"load error %@",error);
}
    
//執(zhí)行JS方法
- (void) doJavaScriptFunction:(NSString *)jsfunction{
    [self.jsContext evaluateScript:jsfunction];
    
}

- (void) sayHello{
    [self showAlertDesc:@"hello world"];
}
    
- (void) setTitle:(NSString *)title{
    [self showAlertDesc:@"setTitle"];
}
    
//下面是大于等于兩個(gè)參數(shù)的情況,要注意這個(gè)格式和JS中的格式對比    
- (void) setTitle:(NSString *)title Left:(NSString *)leftTitle{
    [self showAlertDesc:@"setTitleLeft"];
}
    
- (void) setTitle:(NSString *)title Left:(NSString *)leftTitle Right:(NSString *)rightTitle{
    [self showAlertDesc:@"setTitleLeftRight"];
}
    
- (void) showAlertDesc:(NSString *)desc{
    UIAlertController *alertController  = [UIAlertController alertControllerWithTitle:@"" message:desc preferredStyle:UIAlertControllerStyleAlert];
    
    UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
    [alertController addAction:okAction];
    [self presentViewController:alertController animated:YES completion:nil];
}
    
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

test.html

<!DOCTYPE html>
<html>
    <head>
        <style>
            .button{
                background-color: #d9edf7!important;
                display:block;
                margin-top:40px;
                height:30px;
            }
        </style>
    </head>
    <body>
        <a href = "#" class = "button" onclick = "sayHello()">Say Hello</a>
        <a href = "#" class = "button" onclick = "setTitle()">Set Title</a>
        <a href = "#" class = "button" onclick = "setTitle2()">Set Title Left</a>
        <a href = "#" class = "button" onclick = "setTitle3()">Set Title Left Right</a>
    </body>
    
    <script type="text/javascript">
        function sayHello(){
            demoapi.sayHello();
        }
    
        function setTitle(){
            demoapi.setTitle("Title1");
        }
    
        //下面是大于等于兩個(gè)參數(shù)的情況,要注意這個(gè)格式和JS中的格式對比
        function setTitle2(){
            demoapi.setTitleLeft("Title2","LeftTitle2");
        }
    
        function setTitle3(){
            demoapi.setTitleLeftRight("Title3","LeftTitle3","RightTitle3");
        }
    </script>
</html>

WKWebView

WKWebView作為蘋果官方推薦的Web控件,在UIWebView的基礎(chǔ)上進(jìn)行重構(gòu),加載速度和內(nèi)存開銷上都有很大的提升,當(dāng)我們需要集成該控件時(shí),需要去注意改控件提供和方法和UIWebView的不同。需要注意一點(diǎn),這個(gè)控件要求系統(tǒng)版本最低是IOS8,如果你的項(xiàng)目需要覆蓋IOS8一下的用戶的話,還是乖乖的去用UIWebView吧。

詳細(xì)情況后續(xù)補(bǔ)充

SFSafariView

SFSafariView給人直觀的印象是把Safari內(nèi)嵌到了APP當(dāng)中,目前筆者還沒有接觸過,這里就不在敘述更多的信息了,如果后面結(jié)果的話,會(huì)再寫一篇博客進(jìn)行詳細(xì)說明。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Webview和JS交互方式 前言 現(xiàn)在很多App里都內(nèi)置了Web網(wǎng)頁(Hybrid App),比如說很多電商平臺(tái)...
    badcyc閱讀 301評論 1 2
  • 一、WebView WebView就是一個(gè)內(nèi)嵌瀏覽器控件,在iOS中主要有兩種WebView:UIWebView和...
    iOS祎閱讀 1,237評論 0 2
  • 前言 關(guān)于UIWebView的介紹,相信看過上文的小伙伴們,已經(jīng)大概清楚了吧,如果有問題,歡迎提問。 本文是本系列...
    CoderLF閱讀 9,299評論 2 12
  • 有時(shí)我們有這樣的需求:需要從app的網(wǎng)頁中點(diǎn)擊調(diào)用軟件內(nèi)部的頁面,又或者是軟件內(nèi)部調(diào)用網(wǎng)頁的js方法。 js代碼調(diào)...
    雞蛋掉了閱讀 722評論 2 8
  • 移動(dòng)應(yīng)用中許多復(fù)雜的且經(jīng)常改動(dòng)的頁面會(huì)使用H5進(jìn)行代替native,這里就會(huì)使用到j(luò)s和webview的交互 iO...
    黃成閱讀 11,111評論 0 30

友情鏈接更多精彩內(nèi)容