[iOS] 在 Scroll View 內擺放Web view

有時候你會有自己的想法去安排手機上網頁的顯示,比方說往下拉會有個按鈕,中間是網頁文字,上面可能是廣告或圖片,全都放在一個UISrollView裡面,而且你希望不要分開scroll,而是整體一起拉,scroll在整個畫面上,橫亙整個廣告圖片文字按鈕,那你就會有這樣的需求。

預備動作

當你在用UIWebView時,你會發現呈現出來的結果也是可以拉動的,這是因為UIWebView本身裡面是有scroll特性在的,為了避免scroll混在一起,你要先把 UIWebView 本身的捲動功能關掉:

self.webView.scrollView.scrollEnabled = NO;

注意到我這邊的寫法,我是將webView在xib內拉好,設定好Autolayout,在xib內,我將webView放在scrollView元件下,作為其subView。如果你是打算動態生成webView元件,那你記得要將這元件加到scrollView之中,這非常合理,因為這是你捲動畫面內容的其中一部分啊。

UIWebView *yourWebView; // please initialization by yourself
[self.scroll addSudview: yourWebView];

只是我是在xib內拉好,所以系統會知道那個IBOutlet的webView元件是Scroll view的subview。

到目前為止我們做了兩件事情:
  1. 關閉web view的scroll功能
  2. 將web view設定成scroll view的subview

 Programming

接下來我打算作的事情是這樣,scroll view最上方是圖片,使用image view呈現,中間是網頁文字,就是web view的部份,最下面是一個button,在autolayout內我設定他和web view 之間有隔了13px(會特別提這個是有原因的)。

再來的事情就是一些coding了,根據情況不同,有一些部分會不太一樣,主要順序是這樣:
  1. 在web view讀取完他要的資料之後,要開始改變web view和scroll view的尺寸。
  2. 將web view的尺寸拉開,讓他完整呈現他的內容。
  3. 根據web view尺寸的改變重新設定其他元件與其的相對位置。
實際上你要處理的其實就是WebView的尺寸,你可能讀一個URL,或是一個html的字串,所以每次呈現的內容量是不同的,高度也會不同(我規定寬度必須相同於scroll view),所以大家都必須依著web view去作改變。

改變的時機,就是web view讀取完資料的時候,請將你的View Controller遵守UIWebViewDelegate,我們要在讀取完時作剩下的工作:

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    NSLog(@"Finished loading");
   
    if (![webView isEqual:self.webView) { // 如果不是我的webView元件就跳回
        return;
    }
    float totalHeight = 0; // 用來儲存總高度,等下要設定scroll view的高度




    //[self.webView sizeToFit];
    //NSString *heightString = [self.webContent stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"body\").offsetHeight;"];
    CGRect frame = self.webView.frame;
    frame.size.height = 1;
    self.webView.frame = frame;
    CGSize contentFrame = [webView sizeThatFits:CGSizeZero];

    frame.size = contentFrame;
    self.webView.frame = frame;
    float webHeight = frame.size.height;
    float imageHeight = self.webImageView.bounds.size.height;
   
    totalHeight = webHeight + imageHeight + self.btn.bounds.size.height;
    // 設定scroll view的內容高度
    [self.scrollView setContentSize:CGSizeMake(self.scrollView.frame.size.width, totalHeight)];
    // 設定按鈕位置
    [self.btn setFrame:CGRectMake(28, self.webImageView.bounds.size.height + 13 + self.webContent.bounds.size.height, self.btn.bounds.size.width, self.btn.bounds.size.height)];
}

在我看的教學文章裡面,請大家注意到我程式碼中間有紅色、藍色和紫色的三行,那三行各有人使用,紅色那行sizeToFit有些人說無效果,有人說有效,他是會直接調整你的web view的尺寸,藍色那行是很多人會使用js語言得到高度,紫色這行是會回傳你內容高度,你選擇其中一種使用即可,看看哪行對你有效果。

另外,由於這篇文章是使用auto layout去做的,雖然跟官方文件提到的autolayout和scrollView混用的作法不一樣(不是mix也不是pure),因為最後還是設定了contentSize。

Auto Layout setup

順便一提Auto Layout的設定的部份,簡單說,你xib內你拉好一個scroll view,然後scroll view裡面放一個UIView作為容器,其他要放進scroll view的元件都放到這個UIView內,就叫他content view吧,這個content view是唯一的scroll view的subview。然後你把元件放到content view裡面去。

接下來,建立以下元件之間的constraints:
  • scroll view ----- scroll view的super view (四個邊)
  • content view ----- scroll view (四個邊)
  • content view內元件 ----- content view
這關係絕對不可以搞錯,萬一你把content view和scroll view的super view之間建立constraint,可能會產生不可預知的狀況。

另一種Auto layout的調整方法

將UIWebView的高度constraint建立一個接口:

    @property (weak, nonatomic) IBOutlet NSLayoutConstraint *webViewHeightConstraint;

然後在webViewDidFinishLoad:裡面重新設定他就可以了:

    self.webViewHeightConstraint.constant = self.webView.scrollView.contentSize.height;

透過將webView的scroll view的contentSize高度來獲得真正的內容高度。這樣上面就不用使用sizeToFit這樣的方法了。

如果你的html string裡面有圖片,然後你有調整圖片,那你恐怕不能使用self.webView.scrollView.contentSize,因為他提供的是圖片原始尺寸下的高度,所以,你必須要透過javascript指令來獲取目前的高度:

self.webViewHeightConstraint.constant = [[self.webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"document.height"]] floatValue];

留言