UIWebView的ios和js的交互(一)

githubDemo:
https://github.com/wangjinshan/JSWebDemo

項目展示

![F106F120-3B49-410F-8656-16AE08F8160A.png](http://upload-images.jianshu.io/upload_images/2845360-34023a8a8e63fe1c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
9EE9D7D7-ADA9-4827-A6D4-F2908D379622.png

![7D18E6EB-A9BF-4CF4-9E97-71B8E4E32CF9.png](http://upload-images.jianshu.io/upload_images/2845360-b4173b6d118a0e15.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

E52E3FEB-D13F-4F44-818A-044287CB56A9.png

認識UIWebView

UIWebView繼承自uiview 是用來加載網頁的類,可以簡單理解成就是一個view
完整的UIWebView創建
1加載網絡數據

self.webView = [[UIWebView alloc] initWithFrame:self.view.frame];
    NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://lianghui.huanqiu.com/2017/roll/2017-03/10304840.html"]];
    [self.view addSubview: _webView];
    [_webView loadRequest:request];

2, 加載本地的數據

    NSString *path =[[NSBundle mainBundle]pathForResource:@"JSShareSDKDemo" ofType:@"html"];
    NSURL *url =[NSURL URLWithString:path];
    NSURLRequest *request =[NSURLRequest requestWithURL:url];
    self.webView =[[UIWebView alloc]initWithFrame:self.view.frame];
    [self.webView loadRequest:request];
    [self.view addSubview:self.webView];

加載

加載的方式給了以下三種

/**通過NSURLRequest去加載html界面**/
- (void)loadRequest:(NSURLRequest *)request;
/**加載html格式的字符串,其中的baseUrl下面會介紹**/
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
/**這種方式表示沒見到過,我也不知道是什么,有興趣的可以自己去查查**/
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL;(*很少用*)

-(void)loadRequest:(NSURLRequest *)request 方法即可以去通過網絡連接加載html資源,也可以去加載本地的html資源

   //    加載網絡html
    NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://shenzoom.com"]];
    [self.myWebview loadRequest:request];
  • (void) loadHTMLString:(NSString )string baseURL:(nullable NSURL )baseURL方法一般用來加載本地的html界面
-(void)loadHtmlWithString
{
//    加載css
    NSURL *cssPath = [[NSBundle mainBundle]URLForResource:@"ShareSDK" withExtension:@"css"];
//    創建css標簽
    NSString *css = [NSString stringWithFormat:@"<link href =\"%@\" rel = %@>",cssPath,@"\"stylesheet\""];
    NSLog(@"--css--%@",css);
    NSString *html =[NSString stringWithFormat:@"<html><head>%@</head><body><p>網頁中的文字</p> <button id=\"login\" onclick=\"login()\">點擊按鈕</button> <img src=\"%@\" alt="">   </body></html>",css,css];
    [self.myWebview loadHTMLString:html baseURL:nil];
}

注意:baseURL用來確定htmlString的基準地址,相當于HTML的<base>標簽的作用,定義頁面中所有鏈接的默認地址。具體查看W3C上的base標簽。baseURL是HTML字符串中引用到資源的查找路徑,當HTML中沒有引用外部資源時,可以指定為nil;若引用了外部資源(外部資源:除了html代碼以外,界面中所有的圖片,鏈接都屬于外部資源),一般情況下使用mainBundle的路徑即可。在實際操作中,常常會出現「文本顯示正常,圖片無法顯示」等情況,若HTML文本中引用外部資源都是使用相對路徑,則出現這種問題的原因一般都是baseURL參數錯誤

屬性

//    **webView的代理**
    @property (nullable, nonatomic, assign) id <UIWebViewDelegate> delegate;
//    **內置的scrollView**
    @property (nonatomic, readonly, strong) UIScrollView
//    *scrollView NS_AVAILABLE_IOS(5_0);
//    **URL請求**
    @property (nullable, nonatomic, readonly, strong) NSURLRequest *request;
//    **是否縮放到適合屏幕大小**
    @property (nonatomic) BOOL scalesPageToFit;
//    **執行javaScript操作**
    - (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
加載屬性
- (void)reload; //重新加載數據
- (void)stopLoading; //停止加載數據
@property (nonatomic, readonly, getter=isLoading) BOOL loading; //是否正在加載
- (void)goBack; //返回上一級
- (void)goForward; //跳轉下一級
@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack; //能否返回上一級
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward; //能否跳轉下一級
多媒體屬性
//YES,自動檢測網頁上的電話號碼,單擊可以撥打,_ 已經廢棄_
@property (nonatomic) BOOL detectsPhoneNumbers NS_DEPRECATED_IOS(2_0, 3_0);
//設置某些數據變為鏈接形式,這個枚舉可以設置如電話號,地址,郵箱等轉化為鏈接
@property (nonatomic) UIDataDetectorTypes dataDetectorTypes NS_AVAILABLE_IOS(3_0);
//設置是否使用內聯播放器播放視頻
@property (nonatomic) BOOL allowsInlineMediaPlayback NS_AVAILABLE_IOS(4_0); // defaults to YES
//設置視頻是否自動播放
@property (nonatomic) BOOL mediaPlaybackRequiresUserAction NS_AVAILABLE_IOS(4_0); // default to YES
//設置音頻播放是否支持ari play功能
@property (nonatomic) BOOL mediaPlaybackAllowsAirPlay NS_AVAILABLE_IOS(5_0); //  default to YES
//設置是否將數據加載如內存后渲染界面
@property (nonatomic) BOOL suppressesIncrementalRendering NS_AVAILABLE_IOS(6_0); //  default to NO
//設置用戶交互模式
@property (nonatomic) BOOL keyboardDisplayRequiresUserAction NS_AVAILABLE_IOS(6_0);

iOS7.0 新特性
@property (nonatomic) UIWebPaginationMode paginationMode NS_AVAILABLE_IOS(7_0);
這個屬性用來設置一種模式,當網頁的大小超出view時,將網頁以翻頁的效果展示,枚舉如下:
typedef NS_ENUM(NSInteger, UIWebPaginationMode) 
{ 
  UIWebPaginationModeUnpaginated, //不使用翻頁效果 
  UIWebPaginationModeLeftToRight, //將網頁超出部分分頁,從左向右進行翻頁 
  UIWebPaginationModeTopToBottom, //將網頁超出部分分頁,從上向下進行翻頁 
  UIWebPaginationModeBottomToTop, //將網頁超出部分分頁,從下向上進行翻頁 
  UIWebPaginationModeRightToLeft //將網頁超出部分分頁,從右向左進行翻頁
} __TVOS_PROHIBITED;
//設置每一頁的長度
@property (nonatomic) CGFloat pageLength NS_AVAILABLE_IOS(7_0);
//設置每一頁的間距
@property (nonatomic) CGFloat gapBetweenPages NS_AVAILABLE_IOS(7_0);
//獲取分頁數
@property (nonatomic, readonly) NSUInteger pageCount NS_AVAILABLE_IOS(7_0);
// 未知
@property (nonatomic) UIWebPaginationBreakingMode paginationBreakingMode NS_AVAILABLE_IOS(7_0);
typedef NS_ENUM(NSInteger, UIWebPaginationBreakingMode) 
{ 
  UIWebPaginationBreakingModePage,       
  UIWebPaginationBreakingModeColumn
} __TVOS_PROHIBITED;
iOS9.0新特性
//是否允許畫中畫播放/調用系統的視頻播放器
@property (nonatomic) BOOL allowsPictureInPictureMediaPlayback NS_AVAILABLE_IOS(9_0);
@property (nonatomic) BOOL allowsLinkPreview NS_AVAILABLE_IOS(9_0); //
UIWebView的代理方法 UIWebViewDelegate
self.myWebview.delegate =self;
//代理方法
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    /**返回YES,進行加載。通過UIWebViewNavigationType可以得到請求發起的原因
     如果為webView添加了delegate對象并實現該接口,那么在webView加載任何一個frame之前都會delegate對象的該方法,該方法的返回值用以控制是否允許加載目標鏈接頁面的內容,返回YES將直接加載內容,NO則反之。并且UIWebViewNavigationType枚舉,定義了頁面中用戶行為的分類,包括;
     UIWebViewNavigationTypeLinkClicked,0 用戶觸擊了一個鏈接。
     UIWebViewNavigationTypeFormSubmitted,1 用戶提交了一個表單。
     UIWebViewNavigationTypeBackForward,2, 用戶觸擊前進或返回按鈕。
     UIWebViewNavigationTypeReload,3, 用戶觸擊重新加載的按鈕。
     UIWebViewNavigationTypeFormResubmitted,4,用戶重復提交表單
     UIWebViewNavigationTypeOther,5, 發生其它行為。
     */
//    NSLog(@"----request-------%@",request);
    NSLog(@"----navigationType-------%ld",(long)navigationType);
    return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView
{
    //開始加載,可以加上風火輪(也叫菊花)
}

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    //完成加載
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    //加載出錯
}
禁用頁面滾動彈跳
self.myWebview.scrollView.bounces = false;  //禁用頁面滾動彈跳

基于UIWebView ios與js交互

ios 原聲界面上加載webview, 然后需要和web界面進行數據交互就需要用到 ios和js 交互的知識, 交互的方法有三種:
1, 今天要講解的基于 UIWebView的 交互,這種方式最主要的思想就是通過網頁的代碼方法進行url的攔截操作,后面細講
2 WKWebView 的第三方庫
3, 蘋果為我們封裝 JavaScriptCore.framework

實例講解

1,配置基本環境
創建 Sample.html ShareSDK.css ShareSDK.js 實現界面布局并加載到本地

  代理  UIWebViewDelegate
  NSString *path = [[NSBundle mainBundle]pathForResource:@"Sample" ofType:@"html"];
    NSURL*htmlURL = [NSURL fileURLWithPath:path];
    self.webView =[[UIWebView alloc]initWithFrame:self.view.bounds];
    NSURLRequest *request =[NSURLRequest requestWithURL:htmlURL];
    [_webView loadRequest:request];
    _webView.delegate = self;
    [self.view addSubview:_webView];

以下以在html上集成集成 ShareSDK 為例子, 由于前期數據傳輸沒整理好,大家先忽略數據傳遞的問題,文章后面會補充
1, ShareSDK 初始化 ,創建一個 名字叫ShareSDK的對象 并在里面實現 initSDK初始化方法 ,數據暫時將以數組的形式傳遞,后面再補充上對象json傳遞

function ShareSDK()
{
//    初始化sdk, 注意 此處必須寫上this 否則外部調用失敗
    this.initSDK = function()
    {
        //1,平臺的參數
        var mobkey = 'iosv1101';
        var platformID = new PlatformID();
        //平臺數組
        var platformArr = [platformID.platformID.WeChat,platformID.platformID.WeChatFavorites,platformID.platformID.WeChatMoments];
        //2,微信appkey
        var platformConfig = ["wx4868b35061f87885","64020361b8ec4c99936c0e3999a9f249"];
        //發送請求
        window.location.href = '&initSDK' + '&mobkey'+mobkey +'&platformArr'+ platformArr +'&platformConfig' + platformConfig;
    }
}
var $sharesdk = new ShareSDK();

大家先簡單理解這個就是一個 js方法,下面我們要做的就是在 ios上調用這段代碼
我們已經在 viewDidLoad 的方法中加載了網頁 下面我們就簽UIWebView協議 UIWebViewDelegate 并實現代理方法

ios 調用 js代碼

ios 調用js 代碼非常簡單,UIWebView 已經為我們封裝好了方法(stringByEvaluatingJavaScriptFromString),我們將在網頁加載完畢后進行代碼處理,注意必須在網頁加載完畢才能操作,否則無效

// 網頁完成加載
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
//  注意stringByEvaluatingJavaScriptFromString必須保證是在主線線程中完成任務
    [self.webView stringByEvaluatingJavaScriptFromString:@"window.$sharesdk.initSDK()"];
    NSLog(@"----1------%@",[NSThread currentThread]);   //線程是1
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_sync(queue, ^{
        NSLog(@"----2-----%@",[NSThread currentThread]); //線程還是1
         [self.webView stringByEvaluatingJavaScriptFromString:@"window.$sharesdk.ajsTest()"];
    });
}

注意 (stringByEvaluatingJavaScriptFromString) 這個方法必須在主線程調用,否則崩潰,如下

Tried to obtain the web lock from a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread. Crashing now...

代碼注釋: ios 調用js 相當于 給 js 發送一段字符串,然后程序會根據字符串去環境中尋找對應的方法并執行, @"window.$sharesdk.initSDK()" window js是環境中的全局變量, 所有 js環境中方法和屬性 ,都可以通過 window 來進行調用, 此處 window 去找到 sharesdk對象并執行 initSDK()的方法
檢查 方法有沒有被調用 可以在 initSDK(); 進行 alert();進行測試,彈出警告窗口則表示調用成功

js 調用 ios代碼

js調用 ios 也非常的簡單,就是js發送一段請求,然后ios 在代理方法中進行攔截,如下

 this.initSDK = function()
    {
        //發送請求
        window.location.href = '要發送的數據';
    }

js 中 只要遇到 window.location.href 就會通知 ios 執行回調方法, html 進行 某個時段進行固定跳轉也是經過這個方法, 下面我們就可以到 ios 對這段數據進行解析

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    [self analyzeURL:request.URL];
    return YES;   // 必須返回 yes
}

以上方法中的 request 就攜帶了 window.location.href = '要發送的數據'; 這個方法中 攜帶的 [要發送的數據], 這是一段 json 數據 ,解析這段json數據 你就可以進行相關的操作了, 到此 ios調用 js is調用ios結束,感覺非常簡單, 但是你發現沒有, 以上的實現并沒有牽扯到數據, 個人認為 交互最蛋疼一點就是數據傳遞, UIWebView 并不友好, 數據傳遞比較蛋疼

js傳遞數據給ios

上面說過 js 調用 ios 是通過 發送請求的方法 (window.location.href = '要發送的數據';) 但是這個數據到底應該怎么寫? 如下

//    測試傳遞對象類型
    this.ajsTest = function ()
    {
        var backJson = {};
        var wjs =
        {
            "name":"神族科技CEO",
            "name":"金山",
            "city":"上海"
        };
        backJson["wjs"] = wjs;
        var wjsJson =  ObjectToJsonString(backJson);
        window.location.href ="ajstest://?"+wjsJson;    // 注意協議頭必須是小寫 大寫將轉換成小寫
    }

通常為了解析方便,我們都會攜帶一個協議,這個協議頭可以隨便寫,然后拼接你要的數據,上面的例子 傳遞的是 一個js 中的對象,我們需要將 對象轉換成 js 格式的字符串, 方法就是

var JsonStringToObject = function (string)
{
    try
    {
        return eval("(" + string + ")");
    }
    catch (err)
    {
        return null;
    }
};

這樣就可以把js中的對象數據傳給ios使用,ios攔截到的依然是個 json的字符串需要你手動解析

ios傳遞數據給js

ios 傳遞數據給 js 實現和ios 調用js 代碼一樣,只需要將傳遞的數據拼接在后面就可以

[errorDic setObject:[NSDictionary dictionaryWithObjectsAndKeys:
                                         [NSNumber numberWithInteger:[error code]],
                                         @"error_code",
                                         [error userInfo],
                                         @"error_msg",
                                         nil]
                                 forKey:@"error"];
                    [self.webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"$sharesdk.callBackData('%@')",[self jsonStringFromObject:errorDic]]];
/**
 *  對象序列化為Json字符串
 *
 *  @param object 任意對象
 *
 *  @return Json字符串
 */
- (NSString *)jsonStringFromObject:(id)object
{
    NSString *jsonString = [[NSString alloc]init];
    NSError *error;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:object
                                                      options:NSJSONWritingPrettyPrinted
                                                        error:&error];
    if (! jsonData) {
        NSLog(@"error: %@", error);
    } else {
        jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    }
    NSMutableString *mutStr = [NSMutableString stringWithString:jsonString];
    NSRange range = {0,jsonString.length};
    [mutStr replaceOccurrencesOfString:@" "withString:@""options:NSLiteralSearch range:range];
    NSRange range2 = {0,mutStr.length};
    [mutStr replaceOccurrencesOfString:@"\n"withString:@""options:NSLiteralSearch range:range2];
    return mutStr;
}

這樣就是實現了 ios 把一個 ios對象 轉換成json 對象并 傳給 js 使用
到此 基于 UIWebView 的 ios與js交互全部結束
完美的分割線


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

推薦閱讀更多精彩內容