隨說 : 最近有個需求,是將公司的一個內(nèi)網(wǎng)的頁面嵌套在app中作為一個模塊.這不是很簡單的webView請求一下就行了么?其實內(nèi)里大有乾坤.自己也將思路整理一遍
UIWebView##
UIWebView的基本使用方法 :###
就這樣就已經(jīng)整整個baidu的頁面展示到app上
下面我們看一下webView的屬性與方法
UIWebView *webView = [[UIWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.view = webView;
NSURL *url = [NSURL URLWithString:@"https://www.baidu.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];
UIWebView的層級結(jié)構(gòu) :###
UIWebView的屬性 :###
// 代理屬性 重點需要知道代理方法的使用
@property (nullable, nonatomic, assign) id <UIWebViewDelegate> delegate;
// 這個是webView內(nèi)部的scrollView 只讀,但是利用這個屬性,設(shè)置scrollView的代理,就可以控制整個webView的滾動事件
@property(nonatomic, readonly, strong) UIScrollView *scrollView;
// webView的請求,這個屬性一般在整個加載完成后才能拿到
@property (nullable, nonatomic, readonly, strong) NSURLRequest *request;
// A Boolean value indicating whether the receiver can move backward. (read-only)
// If YES, able to move backward; otherwise, NO.
// 如果這個屬性為YES,才能后退
@property (nonatomic, readonly, getter=canGoBack) BOOL canGoBack;
// A Boolean value indicating whether the receiver can move forward. (read-only)
// If YES, able to move forward; otherwise, NO.
// 如果這個屬性為YES,才能前進
@property (nonatomic, readonly, getter=canGoForward) BOOL canGoForward;
// A Boolean value indicating whether the receiver is done loading content. (read-only)
// If YES, the receiver is still loading content; otherwise, NO.
// 這個屬性很好用,如果為YES證明webView還在加載數(shù)據(jù),所有數(shù)據(jù)加載完畢后,webView就會為No
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
//A Boolean value determining whether the webpage scales to fit the view and the user can change the scale.
//If YES, the webpage is scaled to fit and the user can zoom in and zoom out. If NO, user zooming is disabled. The default value is NO.
// YES代表網(wǎng)頁可以縮放,NO代表不可以縮放
@property (nonatomic) BOOL scalesPageToFit;
// 設(shè)置某些數(shù)據(jù)變?yōu)殒溄有问剑@個枚舉可以設(shè)置如電話號,地址,郵箱等轉(zhuǎn)化為鏈接
@property (nonatomic) UIDataDetectorTypes dataDetectorTypes NS_AVAILABLE_IOS(3_0);
// iPhone Safari defaults to NO. iPad Safari defaults to YES
// 設(shè)置是否使用內(nèi)聯(lián)播放器播放視頻
@property (nonatomic) BOOL allowsInlineMediaPlayback NS_AVAILABLE_IOS(4_0);
// iPhone and iPad Safari both default to YES
// 設(shè)置視頻是否自動播放
@property (nonatomic) BOOL mediaPlaybackRequiresUserAction NS_AVAILABLE_IOS(4_0);
// iPhone and iPad Safari both default to YES
// 設(shè)置音頻播放是否支持ari play功能
@property (nonatomic) BOOL mediaPlaybackAllowsAirPlay NS_AVAILABLE_IOS(5_0);
// iPhone and iPad Safari both default to NO
// 設(shè)置是否將數(shù)據(jù)加載入內(nèi)存后渲染界面
@property (nonatomic) BOOL suppressesIncrementalRendering NS_AVAILABLE_IOS(6_0);
// default is YES
// 設(shè)置用戶是否能打開keyboard交互
@property (nonatomic) BOOL keyboardDisplayRequiresUserAction NS_AVAILABLE_IOS(6_0);
/* IOS7 */ 以后的新特性
// 這個屬性用來設(shè)置一種模式,當(dāng)網(wǎng)頁的大小超出view時,將網(wǎng)頁以翻頁的效果展示,枚舉如下:
@property (nonatomic) UIWebPaginationMode paginationMode NS_AVAILABLE_IOS(7_0);
typedef NS_ENUM(NSInteger, UIWebPaginationMode) {
UIWebPaginationModeUnpaginated, //不使用翻頁效果
UIWebPaginationModeLeftToRight, //將網(wǎng)頁超出部分分頁,從左向右進行翻頁
UIWebPaginationModeTopToBottom, //將網(wǎng)頁超出部分分頁,從上向下進行翻頁
UIWebPaginationModeBottomToTop, //將網(wǎng)頁超出部分分頁,從下向上進行翻頁
UIWebPaginationModeRightToLeft //將網(wǎng)頁超出部分分頁,從右向左進行翻頁
};
// This property determines whether certain CSS properties regarding column- and page-breaking are honored or ignored.
// 這個屬性決定CSS的屬性分頁是可用還是忽略。默認(rèn)是UIWebPaginationBreakingModePage
@property (nonatomic) UIWebPaginationBreakingMode paginationBreakingMode NS_AVAILABLE_IOS(7_0);
// 設(shè)置每一頁的長度
@property (nonatomic) CGFloat pageLength NS_AVAILABLE_IOS(7_0);
// 設(shè)置每一頁的間距
@property (nonatomic) CGFloat gapBetweenPages NS_AVAILABLE_IOS(7_0);
// 獲取頁數(shù)
@property (nonatomic, readonly) NSUInteger pageCount NS_AVAILABLE_IOS(7_0);
還有一些屬性請詳細(xì)翻蘋果文檔
UIWebView的代理方法 :###
UIWebView的代理方法是用的最多的方法,并且一般來說,相對Web頁面作處理都在這相應(yīng)的4個方法中
分別解釋一下方法的調(diào)用情況
// Sent before a web view begins loading a frame.請求發(fā)送前都會調(diào)用該方法,返回NO則不處理這個請求
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
// Sent after a web view starts loading a frame. 請求發(fā)送之后開始接收響應(yīng)之前會調(diào)用這個方法
- (void)webViewDidStartLoad:(UIWebView *)webView;
// Sent after a web view finishes loading a frame. 請求發(fā)送之后,并且服務(wù)器已經(jīng)返回響應(yīng)之后調(diào)用該方法
- (void)webViewDidFinishLoad:(UIWebView *)webView;
// Sent if a web view failed to load a frame. 網(wǎng)頁請求失敗則會調(diào)用該方法
- (void)webView:(UIWebView *)webView didFailLoadWithError:(nullable NSError *)error;
UIWebView的對象方法###
// 加載Data數(shù)據(jù)創(chuàng)建一個webView
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)encodingName baseURL:(NSURL *)baseURL
// 加載本地HTML創(chuàng)建一個webView
- (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL
// 加載一個請求創(chuàng)建一個webView
- (void)loadRequest:(NSURLRequest *)request
// 刷新網(wǎng)頁
- (void)reload;
// 停止網(wǎng)頁加載內(nèi)容
- (void)stopLoading;
// 后退
- (void)goBack;
// 前進
- (void)goForward;
// 執(zhí)行JS方法
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script
WKWebView##
WKWebView的簡介 :###
從文檔中可以看到,這個是IOS8之后新增的一個類,也是蘋果推崇的一個新的類
WKWebView的基本使用方法 :###
其實和UIWebView的用法沒什么區(qū)別
但是WKWebView相對于UIWebView強大了很多,內(nèi)存的消耗相對少了,所提供的接口也豐富了。
推薦使用
多了一部操作就是需要包含webkit框架
@import webkit
WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.view = webView;
NSURL *url = [NSURL URLWithString:@"https://www.baidu.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];
WKWebView的屬性 :###
// UIWebView 中會自動保存Cookie,如果登錄了一次下次再次進入的時候,會記住登錄狀態(tài)
// 在WKWebView中,新增一個configuration屬性, configuration 讓W(xué)KWebView知道登錄狀態(tài),
// configuration 可以通過已有的Cookie進行設(shè)置,也可以通過保存上一次的configuration進行設(shè)置
// WKWebViewConfiguration類中也有一些相應(yīng)的屬性
@property (nonatomic, readonly, copy) WKWebViewConfiguration *configuration;
// The methods of the WKNavigationDelegate protocol help you track the progress of the web site's main frame navigations and decide load policy for main frame and subframe navigations.
// WKWebView中,加入了網(wǎng)站導(dǎo)航的概念,這個對象決定主框架導(dǎo)航加載方法協(xié)議。
@property (nullable, nonatomic, weak) id <WKNavigationDelegate> navigationDelegate;
// The WKUIDelegate class provides methods for presenting native user interface
elements on behalf of a webpage.
// WKWebView中,加入了網(wǎng)站窗口的概念,這個對象決了webView窗口的一些方法協(xié)議。
@property (nullable, nonatomic, weak) id <WKUIDelegate> UIDelegate;
A WKBackForwardList object is a list of webpages previously visited in a web view that can be reached by going back or forward.
// WKWebView中,加入了網(wǎng)站列表的概念,這個WEBBackForwardList對象是以前在Web視圖訪問的網(wǎng)頁,可以通過去后退或前進
@property (nonatomic, readonly, strong) WKBackForwardList *backForwardList;
還有很多方法,同樣可以查文檔看到
WKWebView的代理方法 :###
有一些方法和UIWebView是基本一直的,但是因為返回了navigation,所能用到的屬性多了很多,另外多了一些方法,將請求與相應(yīng)的整個過程
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{
NSLog(@"webViewWebContentProcessDidTerminate: 當(dāng)Web視圖的網(wǎng)頁內(nèi)容被終止時調(diào)用。");
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSLog(@"webView:didFinishNavigation: 響應(yīng)渲染完成后調(diào)用該方法 webView : %@ -- navigation : %@ \n\n",webView,navigation);
}
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSLog(@"webView:didStartProvisionalNavigation: 開始請求 \n\n");
}
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
NSLog(@"webView:didCommitNavigation: 響應(yīng)的內(nèi)容到達主頁面的時候響應(yīng),剛準(zhǔn)備開始渲染頁面應(yīng)用 \n\n");
}
// error
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
// 類似 UIWebView 的- webView:didFailLoadWithError:
NSLog(@"webView:didFailProvisionalNavigation:withError: 啟動時加載數(shù)據(jù)發(fā)生錯誤就會調(diào)用這個方法。 \n\n");
}
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{
NSLog(@"webView:didFailNavigation: 當(dāng)一個正在提交的頁面在跳轉(zhuǎn)過程中出現(xiàn)錯誤時調(diào)用這個方法。 \n\n");
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
NSLog(@"請求前會先進入這個方法 webView:decidePolicyForNavigationActiondecisionHandler: %@ \n\n ",navigationAction.request);
decisionHandler(WKNavigationActionPolicyAllow);
}
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
NSLog(@"返回響應(yīng)前先會調(diào)用這個方法 并且已經(jīng)能接收到響應(yīng)webView:decidePolicyForNavigationResponse:decisionHandler: Response?%@ \n\n",navigationResponse.response);
decisionHandler(WKNavigationResponsePolicyAllow);
}
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{
NSLog(@"webView:didReceiveServerRedirectForProvisionalNavigation: 重定向的時候就會調(diào)用 \n\n");
}
WKWebView的對象方法 :###
這些方法,基本上和UIWebView中的使用用法是一致的,所以
// 這是加載網(wǎng)頁最常用的一種方式,通過一個網(wǎng)頁URL來加載一個WKWebView,這個URL可以是遠(yuǎn)程的也可以是本地的,例如我加載百度的主頁
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
// 根據(jù)一個文件,加載一個WKWebView
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL NS_AVAILABLE(10_11, 9_0);
// 這個方法需要將html文件讀取為字符串從而加載為WKWebView,其中baseURL是我們自己設(shè)置的一個路徑,用于尋找html文件中引用的圖片等素材。
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
// 這個方式使用的比較少,但也更加自由,其中data是文件數(shù)據(jù),MIMEType是文件類型,characterEncodingName是編碼類型,baseURL是素材資源路徑
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL NS_AVAILABLE(10_11, 9_0);
基本使用##
下面會總結(jié)一些我在開發(fā)過程中遇到的坑,和解決問題的一些思路,不過在此之前我發(fā)現(xiàn),如果要webView玩得好,有以下幾點的只是也需要掌握好,因為我認(rèn)為在H5崛起的今天,源生App和H5的交互之間會產(chǎn)生比較大改變,而且源生與H5之間的混編,越來越被重視.所以 :
- 源生技術(shù),特別是有關(guān)于webView這一塊的API要非常熟練,
- js語法, js的語法需要熟練,特別是操作document的幾個常用js,標(biāo)簽需要用得滾瓜爛熟.
- 要非常了解網(wǎng)絡(luò)請求 - 響應(yīng)的機制,理解請求頭,響應(yīng)頭,等等.HTTP的整套協(xié)議
需求一 : 展示一個網(wǎng)頁,但是需要隱藏一部分頁面###
首先看看百度的頁面,這是用Chrome瀏覽器打開的開發(fā)者模式
基本界面組成如下,基本使用用法請詳情百度,這里不作介紹
假設(shè)現(xiàn)在想將這個Logo由網(wǎng)頁開始加載就去掉
百度的logo就是一個div套著一個image標(biāo)簽
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
// 在HTML標(biāo)簽都加載完成后,開始處理HTML標(biāo)簽,調(diào)用JS,操作document
[webView stringByEvaluatingJavaScriptFromString:@"document.getElementById('plus-card').remove();"];
}
就這樣, logo標(biāo)簽就被去掉了,思路就是等HTML加載完成后,操作JS從而操作document標(biāo)簽從而改變整個html頁面的應(yīng)用,下圖是去掉整個Body主題內(nèi)容后的結(jié)果
另外還可以將一段函數(shù)封裝到里面,執(zhí)行函數(shù),原理是通過stringByEvaluatingJavaScriptFromString將JS函數(shù)寫進head標(biāo)簽中,然后再調(diào)用該函數(shù)
// 自定義editMyLogo函數(shù)
[webView stringByEvaluatingJavaScriptFromString:@"var script = document.createElement('script');"
"script.type = 'text/javascript';"
"script.text = \"function editMyLogo() { "
"var logo = document.getElementById('logo');"
"logo.innerHTML= logo.innerHTML + '這是我自己定義的名字';"
"var imglist = logo.getElementsByTagName('IMG');"
"for (i=0 ; i < imglist.length ; i++ ){"
"imglist[i].src = 'http://pic.to8to.com/attch/day_160218/20160218_d968438a2434b62ba59dH7q5KEzTS6OH.png';"
"}"
"}\";"
"document.getElementsByTagName('head')[0].appendChild(script);"];
// 執(zhí)行editMyLogo函數(shù)
[webView stringByEvaluatingJavaScriptFromString:@"editMyLogo();"];
效果如下 :
有幾點問題,這種操作是在webViewDidFinishLoad方法下進行的,webViewDidFinishLoad方法是webView的document已經(jīng)渲染好后,再去處理這個這個頁面.
- 你會發(fā)現(xiàn)有時候會出現(xiàn)一些閃屏現(xiàn)象,原因是渲染過后,內(nèi)部處理JS代碼后,頁面會再渲染一次
- 資源浪費,假設(shè)這邊的需求只需要顯示10%的內(nèi)容,卻要加載100%的內(nèi)容,不過這一方面還需要網(wǎng)頁端作出很好的適配
- 某些時候,JS會失效,不知道什么原因,有些時候自定義加載的JS的方法并沒有執(zhí)行到.等于內(nèi)容并沒有屏蔽
- 等等..
需求二 : 怎樣處理403,404的情況 ?###
@property (nonatomic, assign) BOOL isPost; // 定義一個變量
// 每一個請求開始發(fā)送前都會調(diào)用這個方法
// 1, 定義一個全局變量currentRequest,用作保存當(dāng)前的請求
// 2, 將請求轉(zhuǎn)換成data,然后處理data再將data作為請求數(shù)據(jù)再次請求
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
if (!_isPost) {
NSHTTPURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
if (response.statusCode == 404) {
// 這里處理 404 代碼
} else if (response.statusCode == 403) {
// 這里處理 403 代碼
} else {
_isPost = true;
[webView loadData:data MIMEType:@"text/html" textEncodingName:@"NSUTF8StringEncoding" baseURL:[request URL]];
}
return NO;
}else{
NSLog(@"\n\n shouldStartLoadWithRequest請求準(zhǔn)備 -- %@ \n\n ",request);
_isPost = NO;
return YES;
}
}
需求一 : 進一步改進###
在處理HTML這里,將你想隱藏的頁面,加上 display:none 屬性,
或者,將整段HTML標(biāo)簽去掉.
系列的其他整理
[IOS混合編程 - UIWebView 與 WKWebView . 基本使用 (一)][1]
[IOS混合編程 - Http for IOS (二)][2]
[IOS混合編程 - NSURLProtocol 的使用 (三)][3]
[1]: http://www.lxweimin.com/p/b3e7fa514ab7
[2]: http://www.lxweimin.com/p/a6830a9287d6
[3]: http://www.lxweimin.com/p/ec5d6c204e17