
相比native開發(fā),h5以其相對(duì)成熟、隨時(shí)發(fā)布、開發(fā)成本低等特點(diǎn),成為各大移動(dòng)應(yīng)用采取的通用web方案,在移動(dòng)開發(fā)中有著舉足重輕的地位。而無論Android還是iOS都有專門用來渲染h5頁(yè)面的組件,即WebView。本篇主要從UIWebView開始,談一談iOS的混合開發(fā)。
什么是UIWebView
UIWebView是iOS內(nèi)置的瀏覽器控件,繼承自UIView,遵循UIScrollViewDelegate協(xié)議,方便開發(fā)者監(jiān)聽滾動(dòng)事件。從iOS2.0開始,iOS開發(fā)者就可以使用UIWebView展示web內(nèi)容。使用方法很簡(jiǎn)單,創(chuàng)建一個(gè)UIWebView對(duì)象,添加到當(dāng)前頁(yè)面的根視圖上,然后load你要加載的url或本地資源即可。你還可以通過調(diào)用UIWebView提供的api實(shí)現(xiàn)頁(yè)面的前進(jìn)、后退、刷新等動(dòng)作,甚至你還可以動(dòng)態(tài)的設(shè)置網(wǎng)頁(yè)內(nèi)容。系統(tǒng)應(yīng)用Safari就是采用這個(gè)控件實(shí)現(xiàn)。
UIWebView不但能加載遠(yuǎn)程的網(wǎng)頁(yè)資源,還能加載絕大部分的常見文件,例如:htm/html、txt、pdf/keynote、ppt、doc/docx、音視頻文件等等,可謂功能十分強(qiáng)大!后面將簡(jiǎn)述如何加載不同的文件方法。
UIWebView常用屬性和方法
// 接收加載過程消息的代理對(duì)象,詳見代理方法
@property (nullable, nonatomic, assign) id <UIWebViewDelegate> delegate;
// 與webview關(guān)聯(lián)的滾動(dòng)視圖對(duì)象,通過該對(duì)象去處理webview的滾動(dòng)行為
@property (nonatomic, readonly, strong) UIScrollView *scrollView NS_AVAILABLE_IOS(5_0);
// 當(dāng)前webview加載內(nèi)容位置的url請(qǐng)求
@property (nullable, nonatomic, readonly, strong) NSURLRequest *request;
// web頁(yè)面是否支持縮放來適應(yīng)view視圖,默認(rèn)值為NO,YES表示用戶可以縮放頁(yè)面
@property (nonatomic) BOOL scalesPageToFit;
// 是否可以回退
@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack;
// 是否可以前進(jìn)
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward;
// 是否正在加載
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
// 是否播放內(nèi)聯(lián)視頻,iphone默認(rèn)為NO,設(shè)置為NO時(shí)為全屏播放
@property (nonatomic) BOOL allowsInlineMediaPlayback NS_AVAILABLE_IOS(4_0)
// 是否自動(dòng)播放內(nèi)嵌視屏,默認(rèn)為YES
@property (nonatomic) BOOL mediaPlaybackRequiresUserAction NS_AVAILABLE_IOS(4_0)
// 加載url請(qǐng)求,異步加載
- (void)loadRequest:(NSURLRequest *)request
// 設(shè)置主頁(yè)的內(nèi)容及baseURL,避免不安全url請(qǐng)求
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL
// 設(shè)置頁(yè)面的內(nèi)容、mimeType、encoding以及baseURL
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL
// 重新加載當(dāng)前頁(yè)面
-(void)reload;
// 停止加載任何主幀或子幀正在加載內(nèi)容,如果沒有內(nèi)容加載,則什么也不做
-(void)stopLoading;
// 回退到history中的上個(gè)web頁(yè)面
-(void)goBack;
// 前進(jìn)到history中的下個(gè)頁(yè)面
-(void)goForward;
// 執(zhí)行js語(yǔ)句,返回執(zhí)行結(jié)果
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
UIWebView協(xié)議UIWebViewDelegate
// url請(qǐng)求將要加載時(shí)的代理方法,即webview加載一個(gè)幀之前會(huì)調(diào)用該方法
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
// 開始加載時(shí)的代理方法,即webview已經(jīng)開始加載時(shí)會(huì)調(diào)用該代理方法
- (void)webViewDidStartLoad:(UIWebView *)webView;
// 與上一個(gè)對(duì)應(yīng),完成加載時(shí)的代理方法,即webview完成加載時(shí)會(huì)調(diào)用該代理方法
- (void)webViewDidFinishLoad:(UIWebView *)webView;
// 加載失敗時(shí)的代理方法,即webview加載失敗時(shí)會(huì)調(diào)用該方法,error指示失敗的原因
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;
UIWebView 頁(yè)面加載
在UIWebView提供的接口中,加載頁(yè)面內(nèi)容的方法有3種:
- (void)loadRequest:(NSURLRequest *)request;
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL;
其中,第一種方法用于加載一個(gè)基于文件路徑的URL請(qǐng)求,文件路徑可以是本地,也可以是一個(gè)鏈接。而后面兩種方法一般用于加載本地文件內(nèi)容,正如注釋提到了,避免不安全的url請(qǐng)求。
這里主要介紹一下本地文件的加載方法,UIWebView支持多種格式文件的加載,但是加載方式完全相同。
1. 獲取文件路徑
NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:@"index" withExtension:@"html"];
2. 構(gòu)造URL請(qǐng)求
NSURLRequest *request = [NSURLRequest requestWithURL:fileUrl];
3. 加載請(qǐng)求內(nèi)容
[webView loadRequest:request];
你也可以通過加載本地文件內(nèi)容的方法試一試~
UIWebView的調(diào)試
UIWebView中展示的內(nèi)容,包括html、js、css等,我們很容易通過調(diào)試工具去debug,常用的包括Safari、Chrome。
Safari調(diào)試
準(zhǔn)備工作:
1.打開模擬器(iphone設(shè)備)中的web檢查器功能

2.打開Mac上Safari的開發(fā)者模式

3.通過UIWebView展示一個(gè)web頁(yè)面,然后打開Safari,開發(fā)->模擬器(真機(jī))名->選擇對(duì)應(yīng)的html

Chrome調(diào)試
如何操作見這里:
如何用 Chrome DevTools 調(diào)試 iOS Safari
UIWebView與native交互
h5頁(yè)面不僅用于展示web資源,也提供豐富的用戶交互,包括點(diǎn)擊事件、外部跳轉(zhuǎn)、定位等等,因而與native系統(tǒng)進(jìn)行交互是少不了的。例如最簡(jiǎn)單的場(chǎng)景,由于h5的局限性,無法直接訪問用戶手機(jī)的攝像頭,這個(gè)時(shí)候就可以通過偽協(xié)議約定相應(yīng)接口,通過js調(diào)用oc的方式將動(dòng)作交由native端去實(shí)現(xiàn),然后native完成動(dòng)作后再回調(diào)給h5頁(yè)面,實(shí)現(xiàn)雙方的交互。
UIWebView與native的交互主要通過下面這兩個(gè)方法去實(shí)現(xiàn):
// UIWebView的代理方法
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
// UIWebView的實(shí)例方法
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
操作比較簡(jiǎn)單,大家可以自行嘗試。
其實(shí),專業(yè)的js與oc通信并不是通過UIWebView去和native進(jìn)行的,而是使用包括iOS7.0支持的JavaScriptCore、第三方開源庫(kù)WebViewJavascriptBridge等更加方便、易于擴(kuò)展的方法,通過在兩者之間架構(gòu)一層bridge去滿足雙方的相互調(diào)用。后面將通過專門的文章細(xì)致聊一下常用的oc與js交互方法,為hybrid跨平臺(tái)方案提供應(yīng)用基礎(chǔ)。
UIWebView使用注意事項(xiàng)(坑點(diǎn))
釋放UIWebView對(duì)象之前首先要將其delegate設(shè)置為nil,否則可能會(huì)出現(xiàn)異常crash等問題,一般在dealloc方法中處理;
UIWebView對(duì)象不應(yīng)該被添加到UIScrollView對(duì)像的視圖上,否則可能會(huì)出現(xiàn)無法預(yù)料的問題,因?yàn)橄到y(tǒng)無法正確處理兩個(gè)視圖的點(diǎn)擊、滾動(dòng)、手勢(shì)操作等事件;
加載本地html文件時(shí),確保使用loadHTMLString:baseURL:方法,不要使用loadRequest:方法;
UIWebView的stringByEvaluatingJavaScriptFromString:方法為同步執(zhí)行,當(dāng)前線程將等待該方法執(zhí)行完成,因此調(diào)用該方法將會(huì)導(dǎo)致app出現(xiàn)掛起現(xiàn)象;
小結(jié)
WebView的使用,滿足了業(yè)務(wù)的快速迭代及隨時(shí)發(fā)版,也滿足了web開發(fā)向移動(dòng)端開發(fā)遷移,目前作為通用的混合開發(fā)方案,也變得越來越成熟。但是UIWebView由于自身的問題和不足,一直困擾著開發(fā)者。后面將詳細(xì)介紹UIWebView的替代品——WKWebView,iOS8.0提供的一款基于Webkit的iOS系統(tǒng)瀏覽器,極大改善開發(fā)者和用戶的體驗(yàn)。