1. 程式人生 > >iOS中UIWebView與WKWebView、JavaScript與OC互動、Cookie管理看我就夠(下)

iOS中UIWebView與WKWebView、JavaScript與OC互動、Cookie管理看我就夠(下)

前言

在前面的文章中,我們介紹了UIWebViewWKWebView一些使用,與JS的互動和一些坑,相信看過的小夥伴們,已經大概清楚了吧,如果有問題,歡迎提問。

本文是本系列文章的最後一篇,主要為小夥伴們分享下Safari除錯與前端的配合以及實際應用中一些需求的實現等

關於文中提到的一些內容,這裡我準備了個Demo,有需要的小夥伴可以下載。

本文目錄

  • 前言
  • Safari除錯
    • 開啟Safari開發選單
    • iPhone開啟Web檢查器
    • 執行App
    • 除錯對應的頁面
  • 與前端配合解決bug
  • 實際應用中一些需求的實現
    • 自定義瀏覽器UserAgent
    • Native與H5共享登入狀態
    • Native預覽H5頁面中的image
      • 分析
      • 方案
      • UIWebView實現
      • WKWebView實現
      • 注意
    • Native載入並快取H5頁面中的img
    • Native分享H5頁面到微信、QQ等
    • Native為H5提供一套Native Api(微信、支付寶小程式)
      • 分享
      • 從通訊錄選擇聯絡人
      • 掃描二維碼
  • 總結

Safari除錯

在前面的文章中,檢視網頁的Cookie,其實已經用到了Safari除錯。筆者覺得Safari除錯功能真的很有用,通過它可以輕鬆定位問題的所在。也因此,公司中App一旦有問題出現,不管是客戶端的問題,還是前端的問題,找問題的重任都落到了筆者的身上呢。這一度是一個困擾。想象一下,h5頁面的一個bug,App端幫忙快速定位,並且告知h5相關開發人員該如何修復,是多麼偉大的一件事情。

下面來簡單講講怎麼用Safari除錯。

開啟Safari開發選單

在Mac的Safari偏好設定中,開啟開發選單。具體步驟為:Safari -> 偏好設定… -> 高階 -> 勾選在選單欄顯示“開發”選單


iPhone開啟Web檢查器

具體步驟為:設定 -> Safari -> 高階 -> Web 檢查器


執行App

開啟專案,Cmd + R 執行,開啟想除錯的Web頁面。


除錯對應的頁面

開啟Safari -> 開發 -> 裝置 -> URL。


選中的頁面會變成藍色,點選然後打開了如下的介面。


這個頁面就很像Windows 平臺ChromeF12。可以打斷點:



檢視斷點


檢視Cookie


列印Cookie或者元素


比如我在這裡Alert頁面的title,輸入 alert(document.title);,你會在模擬器中看到彈窗


整體十分有用,操作的體驗跟Xcode很像,小夥伴們自行探索。

與前端配合解決bug

前端有一些問題,在瀏覽器中是無法除錯的,很可能只在App內的瀏覽器中才會復現。這個時候你可以期待前端開發人員會使用XcodeSafari除錯來解決bug,或者靠自己。畢竟大家的目標一致,給使用者提供一個更好的App,解決所有已知問題。

這裡我舉個例子,運用Safari除錯來解決一個前端的bug。

比如新做的h5頁面中,有一個分享按鈕,點選呼叫原生的分享,但是發現,點選之後沒有反應了,什麼問題呢?是Native端實現有問題,還是前端寫的有問題呢?如圖


我們來幫忙看下吧,開啟Safari Web 檢查器,定位到資源,並且在share方法中新增斷點,如圖


會發現,並沒有斷住,而是頁面直接報錯了,仔細檢視錯誤描述,share方法裡多了一個“/”,因此報錯了。當我點選分享按鈕時


會發現,提示找不到變數share。這裡我需要說明一下:

當js中報錯的時候,報錯位置所在的函式以及報錯位置之後的程式碼,都不會執行,所以我點選分享時,提示的是找不到方法,因為js的語法不對,報錯了,這裡解析不出來,所以也就沒有了sharetestAddMethod和之後的函式。

那麼當我點選分享下面的按鈕是,呼叫share下面定義的方法也就會提示找不到對應的函數了。



至此,問題找到了,只要告之前端開發人員即可,讓他修復即可。

實際遇到的問題可能要複雜的多,可以通過斷點,以及控制檯列印一些js變數的值,DOM操作來尋找問題,解決問題。希望可以幫助到小夥伴們。

實際應用中一些需求的實現

自定義瀏覽器UserAgent

這個其實在App開發中,比較重要。比如常見的微信、支付寶App等,都有自己的UserAgent,而UA最常用來判斷在哪個App內,一般App的下載頁中只有一個按鈕"點選下載",當用戶點選該按鈕時,在微信中則跳轉到應用寶,否則跳轉到AppStore。那麼如何區分在哪個App中呢?就是js判斷UA。

//js中判斷
if (navigator.userAgent.indexOf("MicroMessenger") !== -1) {
   //在微信中
}

關於自定義UA,這個UIWebView不提供Api,而WKWebView提供Api,前文中也說明過,就是呼叫customUserAgent屬性。

self.webView.customUserAgent = @"WebViewDemo/1.0.0";    //自定義UA,只支援WKWebView

而有沒有其他的方法實現自定義瀏覽器UserAgent呢?有。

//最好在AppDelegate中就提前設定
@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.

    //設定自定義UserAgent
    [self setCustomUserAgent];
    return YES;
}

- (void)setCustomUserAgent
{
    //get the original user-agent of webview
    UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectZero];
    NSString *oldAgent = [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
    //add my info to the new agent
    NSString *newAgent = [oldAgent stringByAppendingFormat:@" %@", @"WebViewDemo/1.0.0"];
    //regist the new agent
    NSDictionary *dictionnary = [[NSDictionary alloc] initWithObjectsAndKeys:newAgent, @"UserAgent", newAgent, @"User-Agent", nil];
    [[NSUserDefaults standardUserDefaults] registerDefaults:dictionnary];
}

@end

上面的程式碼,展示了在原有UserAgent的基礎上,新增一些自定義的內容。


可以看到原本的UA後面已經有我們新增的內容了WebViewDemo/1.0.0

這裡需要說明的是:

  1. 通過NSUserDefaults設定自定義UserAgent,可以同時作用於UIWebViewWKWebView
  2. WKWebViewcustomUserAgent屬性,優先順序高於NSUserDefaults,當同時設定時,顯示customUserAgent的值。如上圖。

Native與H5共享登入狀態

這個需求在前面的文章中針對WKWebViewUIWebView分別單獨做過介紹。維持登入狀態,依賴的是相同的Cookie

UIWebView實現起來基本不需要做額外的操作,只要保證sharedHTTPCookieStorage中的Cookie是沒問題的。

WKWebView實現起來相對麻煩,有很多坑,這裡不再詳細描述,小夥伴們可以看下上篇文章中Cookie管理一節。

Native預覽H5頁面中的image

這個需求,應該是一個比較常見的需求。在微信中瀏覽網頁時,看到喜歡的圖片,你會點選圖片檢視大圖,然後長按圖片儲存。

分析

如果你的專案中有這樣的需求的話,可能你需要做如下的分析。

  1. 如果想在Native預覽H5中的image,最需要的是什麼?是圖片的連結。如果能有縮圖更好了。
  2. 只要獲取了連結,就可以跳轉到一個ViewController中,預覽圖片,後續長按儲存自然水到渠成。
  3. 那應該如何獲取圖片的連結呢?通過JS -> OC 傳遞圖片url。

這裡,究竟如何實現獲取圖片連結,取決於你用的是UIWebView還是WKWebView

方案

當頁面載入完成後,給html頁面中所有無預設點選事件<img>新增點選事件,當用戶點選時,拿到所有引數。

(其實這不是最好的方案,最好的解決方案是,跟前端約定一下,哪些圖片需要預覽,哪些img標籤的id統一,或者有個特定的屬性,這樣客戶端可以根據id找到這些img標籤)

首先,Html中有個img標籤

![](xxx.jpg)

我先寫好一個ImgAddClickEvent.js檔案,來實現給所有無預設點選事件的<img>新增點選事件。

//獲取所有img標籤
var imgs = document.getElementsByTagName("img");
//獲取所有的imgUrl
var imgUrls = new Array();
var x = 0;
var y = 0;
var width = 0;
var height = 0;
for (var i = 0; i < imgs.length; i++) {
    var img = imgs[i];
    //如果圖片連結存在
    if (img.src || img.getAttribute('data-src')) {
        //新增到圖片連結陣列中
        imgUrls.push(img.src || img.getAttribute('data-src'));
        //如果圖片沒有預設的onclick事件,且父元素不是a標籤,則新增onclick事件,當用戶點選時,把圖片連結回傳給Native
        if (!img.onclick && img.parentElement.tagName !== "A") {
            //給圖片新增下標的屬性
            img.index = i; //記錄下標
            //新增點選事件,並且回傳選中的圖片連結、下標、螢幕上的位置、全部的圖片陣列等
            img.onclick = function() {
                x = this.getBoundingClientRect().left;
                y = this.getBoundingClientRect().top;
                x = x + document.documentElement.scrollLeft;
                y = y + document.documentElement.scrollTop;
                width = this.width;
                height = this.height;
                var imgInfo = {
                    imgUrl: this.src || this.getAttribute('data-src'),
                    x: x,
                    y: y,
                    width: width,
                    height: height,
                    index: this.index,
                    imgUrls: imgUrls
                };
                //UIWebView使用
                h5ImageDidClick(imgInfo);
            }
        }
    }
}

function h5ImageDidClick(info) {
    //WKWebView使用
    window.webkit.messageHandlers.imageDidClick.postMessage(info);
}

下面分別介紹UIWebViewWKWebView如何實現。

UIWebView實現

UIWebView直接使用JavaScriptCore<img>新增onclick方法為OC的實現即可。

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    [self convertJSFunctionsToOCMethods];
}

- (void)convertJSFunctionsToOCMethods {
    //獲取該UIWebview的javascript上下文
    //self持有jsContext
    //@property (nonatomic, strong) JSContext *jsContext;
    self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

      //先注入給圖片新增點選事件的js
    //防止頻繁IO操作,造成效能影響
    static NSString *jsSource;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        jsSource = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"ImgAddClickEvent" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil];
    });
    [self.jsContext evaluateScript:jsSource];
    //替換回調方法
    self.jsContext[@"h5ImageDidClick"] = ^(NSDictionary *imgInfo) {
        NSLog(@"UIWebView點選了html上的圖片,資訊是:%@", imgInfo);
    };
}

WKWebView實現

WKWebView實現,需要使用WKUserScriptscriptMessageHandler,下面簡單介紹下,詳細實現,見Demo。

WKWebViewUIViewController中實現如下

/**
 頁面中的所有img標籤新增點選事件
 */
- (void