UIWebView 詳解

UIWebView用于在App中嵌入網(wǎng)頁(yè)內(nèi)容,通常情況下是html格式的網(wǎng)頁(yè),也支持pdf, word等文檔。

首先讓我們了解一下UIWebView有哪些優(yōu)點(diǎn):

可跨平臺(tái)

開發(fā)一次可以部署iOS、Android等平臺(tái)。

發(fā)布更新快

在服務(wù)器端發(fā)布,能夠?qū)崟r(shí)更新終端展示,便于快速升級(jí)以及緊急修復(fù)bug。

排版布局能力強(qiáng)

強(qiáng)大的HTML+CSS讓人膜拜

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

性能

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

數(shù)據(jù)通訊復(fù)雜

UIWebView與App之間進(jìn)行數(shù)據(jù)通訊只能通過(guò)javascript或者UIWebViewDelegate來(lái)進(jìn)行,客戶端想傳參數(shù)給UIWebView修改網(wǎng)頁(yè)或者從網(wǎng)頁(yè)中獲取數(shù)據(jù)都比較復(fù)雜。

咱們應(yīng)該揚(yáng)長(zhǎng)避短,在以下場(chǎng)景考慮使用UIWebView:

排版復(fù)雜的內(nèi)容

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

需后臺(tái)靈活控制的界面

認(rèn)證、免責(zé)聲明以及PM要求三天兩頭需要變幻莫測(cè)的頁(yè)面等。怎么辦?告訴我地址,UIWebView包起來(lái),結(jié)束:)

原網(wǎng)頁(yè)

查看新浪網(wǎng)、騰訊網(wǎng)等網(wǎng)頁(yè)內(nèi)容,這個(gè)木有辦法,總不能讓我們自己寫一個(gè)瀏覽器功能吧。

UIWebView的常規(guī)使用方法:

加載內(nèi)容

//加載網(wǎng)頁(yè)或者本地文件

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

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

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

//功能與上面類似

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

實(shí)現(xiàn)UIWebViewDelegate

主要使用到的方法

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

{

//在這里截取網(wǎng)絡(luò)請(qǐng)求,可以為所欲為的做你想做的事情:)

//…

}

使用stringByEvaluatingJavaScriptFromString與UIWebView中的網(wǎng)頁(yè)進(jìn)行數(shù)據(jù)通訊,需要你有點(diǎn)javascript功底,也可以去看看js bridge的相關(guān)文章。

上面說(shuō)的東西是不是太俗套,太正統(tǒng)了?下面講點(diǎn)實(shí)際點(diǎn)兒的吧。

科普一下:

UIWebView包含著一個(gè)scrollView,iOS5的時(shí)候已經(jīng)公開,在此之前需寫代碼遍歷UIWebView的subviews把它找出來(lái)。scrollView里面包含著一個(gè)UIWebBrowserView用于渲染網(wǎng)頁(yè)內(nèi)容的,該屬性沒(méi)有公開,需要遍歷其subviews找出來(lái)。

去掉拖動(dòng)到頂部或者底部時(shí)露出來(lái)的漸變顏色

如圖所示:

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];

}

}

}

}

}

結(jié)果:

image

白屏閃爍

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

//在load之前,先設(shè)置兩個(gè)屬性

_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中實(shí)現(xiàn)如下:

#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 = …;

}

在網(wǎng)頁(yè)頂部加上一個(gè)headerView,并隨著網(wǎng)頁(yè)一起滾動(dòng)

由于需要一起滾動(dòng),所以footerView應(yīng)該是_webView.scrollView的subview。上面我們提到過(guò)_webView.scrollView.webBrowserView是用來(lái)渲染網(wǎng)頁(yè)的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,并且正確設(shè)置了webBrowserView的位置后不會(huì)影響到下面3中的contentSize計(jì)算,UIWebView應(yīng)該已經(jīng)考慮到了webBrowserView的位置偏移。

下面紅色區(qū)域即是headerView

image

在網(wǎng)頁(yè)的末尾加上一個(gè)footerView,并且跟著網(wǎng)頁(yè)一塊滾動(dòng)

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

咱們可以利用KVO來(lái)解決這個(gè)問(wèn)題。

//監(jiān)聽(tīng)scrollView的contentSize的變化

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

[self addObserverForWebViewContentSize];

//0.1s后設(shè)置footerView的位置,以防止contentSize沒(méi)有變化

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

}

有同學(xué)不禁要問(wèn):為什么需要監(jiān)聽(tīng)contentSize的變化呢?在webViewDidFinishLoad中直接取_webView.scrollView.contentSize不就可以了嗎?

解答:webViewDidFinishLoad會(huì)被多次回調(diào),因?yàn)榫W(wǎng)頁(yè)中有圖片、表格等多種資源,UIWebView在加載資源的時(shí)候會(huì)不斷調(diào)整contentSize以渲染新加載完成的內(nèi)容。

//contentSize變化時(shí),重新布局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”];

}

//設(shè)置footerView的合理位置

- (void)layoutFooterView{

//取消監(jiān)聽(tīng),因?yàn)檫@里會(huì)調(diào)整contentSize,避免無(wú)限遞歸

[self removeObserverForWebViewContentSize];

CGSize contentSize = _webView.scrollView.contentSize;

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

//設(shè)置footerView的位置

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

[_webView.scrollView addSubview:_footerView];

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

//重新監(jiān)聽(tīng)

[self addObserverForWebViewContentSize];

}

//下圖底部紅色區(qū)域即footerView

image

滑動(dòng)隱藏頂部Bar

如果你想在用戶向上滑動(dòng)時(shí)隱藏頂部bar,向下滑動(dòng)時(shí)顯示頂部bar,該怎么辦呢?

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

怎么辦呀???

好吧,答案就是: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];

}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 創(chuàng)建 githubdemo: https://github.com/wangjinshan/JSWebDemoUI...
    874b526fa570閱讀 712評(píng)論 0 1
  • 1.判斷UIWebView徹底加載完畢 當(dāng)網(wǎng)頁(yè)重定向發(fā)生時(shí),網(wǎng)址被重定向幾次,WebViewDidFinishLo...
    iOS白水閱讀 285評(píng)論 0 0
  • 一、初始化與三種加載方式 第一種 這是加載網(wǎng)頁(yè)最常用的一種方式,通過(guò)一個(gè)網(wǎng)頁(yè)URL來(lái)進(jìn)行加載,這個(gè)URL可以是遠(yuǎn)程...
    楚簡(jiǎn)約閱讀 1,378評(píng)論 0 1
  • 前言 背景:最近做的項(xiàng)目中有這樣一個(gè)需求,一個(gè)話題詳情界面內(nèi)部分內(nèi)容為html標(biāo)簽,其他為普通內(nèi)容,然后html標(biāo)...
    王隆帥閱讀 7,218評(píng)論 11 97
  • UIWebView介紹 UIWebView是iOS內(nèi)置的瀏覽器控件;系統(tǒng)自帶的Safari瀏覽器就是通過(guò)UIWeb...
    037e3257fa3b閱讀 3,777評(píng)論 0 1

友情鏈接更多精彩內(nèi)容