不久以前,項目中用到了UIWebView加載js的功能,以前使用webView都是簡單使用,沒考慮不少與js交互的地方,雖然如今項目完成了,可是回頭看看這方面的知識仍是有些茫然,在此記錄一點,而後後續若是有用更多的話再來進行補充。html
封裝個view,提供給開發者使用,暴露兩個方法以供調用:前端
一、是調用initWithXXX進行初始化位置等等參數配置;web
二、調用loadH5PageWithSuccessBlock:failureBlock:讓view中的webView加載H5頁面顯示出來便可。異步
其主要實現就是webView加載一個URL的過程,給開發者一個加載失敗和完成的回調。ui
一、首先實現的是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中標示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等相關對象移除,避免長時間等待,影響交互體驗。