iOS中UIWebView的一個需求:得到js圖片請求完成的回調時機

不久以前,項目中用到了UIWebView加載js的功能,以前使用webView都是簡單使用,沒考慮不少與js交互的地方,雖然如今項目完成了,可是回頭看看這方面的知識仍是有些茫然,在此記錄一點,而後後續若是有用更多的話再來進行補充。html

需求

封裝個view,提供給開發者使用,暴露兩個方法以供調用:前端

一、是調用initWithXXX進行初始化位置等等參數配置;web

二、調用loadH5PageWithSuccessBlock:failureBlock:讓view中的webView加載H5頁面顯示出來便可。異步

其主要實現就是webView加載一個URL的過程,給開發者一個加載失敗和完成的回調。ui

 

UIWebView的代理方法

一、首先實現的是UIWebView的shouldStartLoadWithRequest:navigationType:代理方法 編碼

注:對於返回的是unicode編碼的字符串,用 stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding 進行返回UTF8編碼的字符串。lua

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    
    NSLog(@"URL : %@", [request.URL.absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]);
    
    // click event
    if ([request.URL.host isEqualToString:@"call_close"])
    {// notify click close
        if (self.gh_clickCloseBlock) {
            self.gh_clickCloseBlock(webView, request.URL.absoluteString);
        }
        [self stopTimer:self.loadStatusCheckTimer];
        return NO;
    }
    
    if ([request.URL.host isEqualToString:@"on_click"])
    {
        NSString *URL_String = [request.URL.path substringFromIndex:1];
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:URL_String]];
        
        // notify click content
        if (self.gh_clickBlock) {
            self.gh_clickBlock(webView, URL_String);
        }
        return NO;
    }
    
    return YES;
}

若是上述回調中返回YES,那麼webView會進行加載html頁面,執行 webViewDidStartLoad: 回調。url

每次在頁面中的點擊均可能會「截獲」該URL,並會再次調用上述回調,那麼經過URL的scheme和host就能夠判斷是什麼動做事件,spa

例如:代理

若是是host爲「call_close」,則能夠作一些關閉的操做:移除view,中止定時器等,並添加關閉頁面的block回調給開發者。

若是是host爲「on_click」,則能夠添加點擊頁面內容的block回調給開發者。

 

二、開始加載的回調

- (void)webViewDidStartLoad:(UIWebView *)webView {
    NSLog(@"start, %d", webView.loading);
}

 

三、若是加載失敗的話會調用 didFailLoadWithError:

頁面加載超時等失敗的時機回調給開發者,可是對於請求中HTTP的狀態碼error的時候並不能判斷出來,

對於這部分具體能夠用NSURLConnection判斷response的statusCode,若是是400以上便可回調失敗,若是是成功響應再容許webView加載這個URL,

咱們能夠只要response,不須要receiveData,因此在獲得response以後將connection cancel掉。

參考相關:http://stackoverflow.com/questions/1061364/how-do-i-get-the-last-http-status-code-from-a-uiwebview

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
    NSLog(@"failure : %@", error.description);
    
    // page加載失敗的callBack
    if (self.gh_loadFailBlock) {
        self.gh_loadFailBlock(webView, error.code);
    }
}

 

四、page加載完成的回調,啓動定時器進行檢查,不斷獲取js中關於image的加載狀態,以便獲取回調時機。

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    NSLog(@"finish, %d", webView.loading);
    
    [webView stringByEvaluatingJavaScriptFromString:@"document.documentElement.style.webkitUserSelect='none';"];
    [webView stringByEvaluatingJavaScriptFromString:@"document.body.style.webkitTouchCallout='none';"];
    
    if(!injectedPageLoadedJS)
    {
        injectedPageLoadedJS = YES;
        [self stopTimer:self.loadStatusCheckTimer];
        self.loadStatusCheckTimer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(checkLoadStatus:) userInfo:nil repeats:YES];
    }
}

加載html的時候,咱們的js會執行對url進行請求的操做,其中會有請求image資源,可是在webView這裏,當html這個page加載完成時就會調用finish回調,若是加載H5頁面的成功回調寫在這裏,可能會出現js尚未成功請求到圖片資源渲染到頁面上,你已經回調開發者加載成功。因此期間從finish到image異步請求完成,中間有段時間須要等待。

解決方式是在頁面加載完成finish以後,經過NSTimer不斷地去獲取js加載image狀態的返回值,直到獲取成功的返回值,作出給開發者的block。

與js簡單交互

和前端協商將js中標示image異步請求成功的變量定爲window.__loadImageStatus__; 這樣js在拿到image請求成功的回調以後,會將此變量賦值爲imgLoaded,這個js的值能夠經過webView的stringByEvaluatingJavaScriptFromString:方法的返回值拿到。 

什麼時候去取到這個值,須要在webView加載完html頁面時finish回調以後取,能夠添加一個定時器,每隔0.5s中經過stringByEvaluatingJavaScriptFromString:拿到返回值,判斷是不是imgLoaded,若是是,說明此時圖片異步加載成功,圖片已經展現,在此能夠做爲給開發者的成功回調。

 

- (void)checkLoadStatus:(NSTimer *)timer {
    
    NSString *evalString = [_webH5View stringByEvaluatingJavaScriptFromString:@"window.__loadImageStatus__;"];
    NSLog(@"+++++js 返回的status值:%@", evalString);
    
    if([evalString isEqualToString:@"imgLoaded"])
    {
        //Web page has finished. Shut down the timer.
        [self stopTimer:timer];
        injectedPageLoadedJS = NO;
        
        NSLog(@"成功獲取到imgLoaded,js將image load 完成的回調值發給");// 成功回調
        if (self.gh_loadAdSuccessBlock) {
            self.gh_loadAdSuccessBlock(_webH5View, _request.URL.absoluteString);
        }
        
        self.count = 0;
    }
    
    else {
        
        if (self.count > 10) {
            [self stopTimer:timer];
            
        } else {
            self.count += 1;
        }
    }
}

 

注:最好添加一個計數器,當10次(也就是5s)尚未獲取到js關於image加載成功的返回值,就能夠將timer等相關對象移除,避免長時間等待,影響交互體驗。

相關文章
相關標籤/搜索