UIWebView 詳解

UIWebView用于在App中嵌入網頁內容,通常情況下是html格式的網頁,也支持pdf, word等文檔。

首先讓我們了解一下UIWebView有哪些優點:

可跨平臺

開發一次可以部署iOS、Android等平臺。

發布更新快

在服務器端發布,能夠實時更新終端展示,便于快速升級以及緊急修復bug。

排版布局能力強

強大的HTML+CSS讓人膜拜

世界上有十全十美的人么?也許只有上帝吧。UIWebView的缺點:

性能

Native先生與HTML5先生爭論時最喜歡說的一句話就是:“你性能不行”(看清楚了哈,不是“性能力不行”)。Web App運行在瀏覽器里,目前瀏覽器的開放能力難以支持HTML5與Native對抗。

數據通訊復雜

UIWebView與App之間進行數據通訊只能通過javascript或者UIWebViewDelegate來進行,客戶端想傳參數給UIWebView修改網頁或者從網頁中獲取數據都比較復雜。

咱們應該揚長避短,在以下場景考慮使用UIWebView:

排版復雜的內容

圖文混排、文字環繞、文章內各種超鏈及高亮顯示。這些讓iOS工程師抓狂的界面,讓web前端小伙伴們搞定,吃頓飯,然后用UIWebView包起來。

需后臺靈活控制的界面

認證、免責聲明以及PM要求三天兩頭需要變幻莫測的頁面等。怎么辦?告訴我地址,UIWebView包起來,結束:)

原網頁

查看新浪網、騰訊網等網頁內容,這個木有辦法,總不能讓我們自己寫一個瀏覽器功能吧。

UIWebView的常規使用方法:

加載內容

//加載網頁或者本地文件

- (void)loadRequest:(NSURLRequest *)request;

//直接加載html內容,如果html中的圖片等資源在本地目錄,注意將baseURL指向該目錄

- (void)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL;

//功能與上面類似

- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL;

實現UIWebViewDelegate

主要使用到的方法

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

{

//在這里截取網絡請求,可以為所欲為的做你想做的事情:)

//…

}

使用stringByEvaluatingJavaScriptFromString與UIWebView中的網頁進行數據通訊,需要你有點javascript功底,也可以去看看js bridge的相關文章。

上面說的東西是不是太俗套,太正統了?下面講點實際點兒的吧。

科普一下:

UIWebView包含著一個scrollView,iOS5的時候已經公開,在此之前需寫代碼遍歷UIWebView的subviews把它找出來。scrollView里面包含著一個UIWebBrowserView用于渲染網頁內容的,該屬性沒有公開,需要遍歷其subviews找出來。

去掉拖動到頂部或者底部時露出來的漸變顏色

如圖所示:

image 解決辦法:

將scrollView中包含的所有UIImageView隱藏

- (void)removeGradientBgColorOfWebView:(UIWebView*)aWebView{

NSArray *subViews = aWebView.subviews;

for (UIView* subView in subViews){

if ([subView isKindOfClass:[UIScrollView class]]) {

for (UIView* shadowView in [subView subviews]){

if ([shadowView isKindOfClass:[UIImageView class]]) {

[shadowView setHidden:YES];

}

}

}

}

}

結果:

image

白屏閃爍

你嵌入的html內容是有背景色的,但是在load的時候還是會有白屏閃爍,在展現你的內容前會出現白色背景,任憑你怎么設置UIWebView或者里面scrollView, webBrowserView的backgroundColor都沒有作用。解決辦法:

//在load之前,先設置兩個屬性

_webView.opaque = NO;

[_webView.scrollView.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

NSString *ss = [NSString stringWithUTF8String:object_getClassName(obj)];

if ([ss isEqualToString:@"UIWebBrowserView"] ) {

[obj setHidden:YES];

*stop = YES;

}

}];

[_webView loadHTMLString:htmlStr baseURL:baseURL];

在UIWebViewDelegate中實現如下:

#pragma mark - UIWebViewDelegate

- (void)webViewDidStartLoad:(UIWebView *)webView{

[_webView.scrollView.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {

NSString *ss = [NSString stringWithUTF8String:object_getClassName(obj)];

if ([ss isEqualToString:@"UIWebBrowserView"] ) {

[obj setHidden:NO];

*stop = YES;

}

}];

}

- (void)webViewDidFinishLoad:(UIWebView *)webView{

_webView.opaque = YES;

_webView.backgroundColor = …;

}

在網頁頂部加上一個headerView,并隨著網頁一起滾動

由于需要一起滾動,所以footerView應該是_webView.scrollView的subview。上面我們提到過_webView.scrollView.webBrowserView是用來渲染網頁的view,所以我們的headerView只要在webBrowserView上面即可。

[_webView.scrollView addSubview:_headerView];

UIView *webBrowserView = [_webView webBrowserView];

CGRect frame = webBrowserView.frame;

frame.origin.y = CGRectGetMaxY(_headerView.frame);

webBrowserView.frame = frame;

注意:在scrollView頂部添加headerView,并且正確設置了webBrowserView的位置后不會影響到下面3中的contentSize計算,UIWebView應該已經考慮到了webBrowserView的位置偏移。

下面紅色區域即是headerView

image

在網頁的末尾加上一個footerView,并且跟著網頁一塊滾動

由于需要一起滾動,所以footerView應該是_webView.scrollView的subview。正常情況下你是不知道網頁被渲染后的高度的,也就是說不知道_webView.scrollView.contentSize,如果知道contentSize就好辦了,直接將footerView添加在末尾。

咱們可以利用KVO來解決這個問題。

//監聽scrollView的contentSize的變化

- (void)webViewDidFinishLoad:(UIWebView *)webView{

[self addObserverForWebViewContentSize];

//0.1s后設置footerView的位置,以防止contentSize沒有變化

[self performSelector:@selector(layoutFooterView) withObject:nil afterDelay:0.1];

}

有同學不禁要問:為什么需要監聽contentSize的變化呢?在webViewDidFinishLoad中直接取_webView.scrollView.contentSize不就可以了嗎?

解答:webViewDidFinishLoad會被多次回調,因為網頁中有圖片、表格等多種資源,UIWebView在加載資源的時候會不斷調整contentSize以渲染新加載完成的內容。

//contentSize變化時,重新布局footerView

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{

if (context == &kContentSizeFlag) {

[self layoutFooterView];

}

}

- (void)addObserverForWebViewContentSize{

[_webView.scrollView addObserver:self forKeyPath:@“contentSize” options:0 context:&kContentSizeFlag];

}

- (void) removeObserverForWebViewContentSize{

[_webView.contentScrollView removeObserver:self forKeyPath:@“contentSize”];

}

//設置footerView的合理位置

- (void)layoutFooterView{

//取消監聽,因為這里會調整contentSize,避免無限遞歸

[self removeObserverForWebViewContentSize];

CGSize contentSize = _webView.scrollView.contentSize;

CGFloat y = CGRectGetMaxY(_webView.webBrowserView.frame);

//設置footerView的位置

_footerView.frame = CGRectMake(0, y, contentSize.width, footerHeight);

[_webView.scrollView addSubview:_footerView];

_webView.scrollView.contentSize = CGSizeMake(contentSize.width, y + footerHeight);

//重新監聽

[self addObserverForWebViewContentSize];

}

//下圖底部紅色區域即footerView

image

滑動隱藏頂部Bar

如果你想在用戶向上滑動時隱藏頂部bar,向下滑動時顯示頂部bar,該怎么辦呢?

_webView.scrollView的delegate是_webView自身,你是不能接管的,所以拿不到scrollView的相關事件。

怎么辦呀???

好吧,答案就是:KVO, contentOffset

- (void)hideNavigationBar:(BOOL)animated { … }

- (void)showNavigationBar:(BOOL)animated { … }

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{

if(context == &kContentOffsetFlag){

CGFloat y = [object contentOffset].y;

UIScrollView *scrollView = _webView.scrollView;

CGPoint p = [scrollView.panGestureRecognizer velocityInView:scrollView];

CGFloat maxY = scrollView.contentSize.height + scrollView.contentInset.top - scrollView.bounds.size.height;

if(fabsf(p.y) < 0.001 && _contentOffsetY - y > 5 && y < maxY - 5){

[self showNavigationBar:YES];

}

else if(p.y < 0 && _webView.scrollView.dragging) {//上滑隱藏

[self hideNavigationBar:YES];

}

else if(p.y > 1500){//快速下滑顯示

[self showNavigationBar:YES];

}

else if(y <= self.navigationBar.bounds.size.height){

[self showNavigationBar:NO];

}

}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 創建 githubdemo: https://github.com/wangjinshan/JSWebDemoUI...
    874b526fa570閱讀 698評論 0 1
  • 1.判斷UIWebView徹底加載完畢 當網頁重定向發生時,網址被重定向幾次,WebViewDidFinishLo...
    iOS白水閱讀 265評論 0 0
  • 一、初始化與三種加載方式 第一種 這是加載網頁最常用的一種方式,通過一個網頁URL來進行加載,這個URL可以是遠程...
    楚簡約閱讀 1,311評論 0 1
  • 前言 背景:最近做的項目中有這樣一個需求,一個話題詳情界面內部分內容為html標簽,其他為普通內容,然后html標...
    王隆帥閱讀 7,149評論 11 97
  • UIWebView介紹 UIWebView是iOS內置的瀏覽器控件;系統自帶的Safari瀏覽器就是通過UIWeb...
    037e3257fa3b閱讀 3,734評論 0 1