en

hi, it seems you are using microsoft internet explorer. it doesn't match web standard and causes problems browsing this site. please please please use mozilla firefox or google chrome instead. thank you!

zh

哦哦!您正在使用Internet Explorer 瀏覽器,它與我們的網頁標準並不相容,可能會導致畫面顯示不正常。
請改用 Mozilla Firefox 或者 Google Chrome 才能正常瀏覽本網站,謝謝!

5.07.2012

使用 UIPageControl 與 UIScrollView 來實作畫面切換的效果

  

使用 UIPageControl 與 UIScrollView 來實作畫面切換的效果,在 iOS 中應該算是最常見的應用。UIPageControl,一個可以顯示目操作狀態 UIControl 元件;UIScrollView 一個提供捲軸功能的 UIView 元件,下面就來看看如何透過這兩個元件來幫助我們達成切換畫面的效果。


實作 UIScrollView 捲動
由於我們要透過 UIScrollView 在捲動時的狀況來設定 UIPageControl,所以必須替實作的 Class 新增 <UIScrollViewDelegate> 協定,才能透過協定內的 scrollViewDidScroll: 方法函式取得 UIScrollView 的捲動狀況。
@interface ViewController : UIViewController <UIScrollViewDelegate>
接下來,分別對兩個元件作初始化的設定,將 UIPageControl 的範圍設定成 5(0~4),並且隱藏 UIScrollView 中所有的捲軸介面,與開啟 Paging 的功能,這樣才能讓使用者在捲頁時是以整個 UIScrollView 為單位,而不是以 Pixel 為單位。  最後我們使用一個 for 迴圈來產生 UIScrollView 中的每個 SubView,其程式碼如下。
//UIPageControl設定
[pageControl setNumberOfPages:5];
[pageControl setCurrentPage:0];

//UIScrollView設定
[scrollView setPagingEnabled:YES];
[scrollView setShowsHorizontalScrollIndicator:NO];
[scrollView setShowsVerticalScrollIndicator:NO];
[scrollView setScrollsToTop:NO];
[scrollView setDelegate:self];

CGFloat width, height;
width = scrollView.frame.size.width;
height = scrollView.frame.size.height;
[scrollView setContentSize:CGSizeMake(width * 5, height)];

//製作ScrollView的內容
for (int i=0; i!=pageControl.numberOfPages; i++) {
    CGRect frame = CGRectMake(width*i, 0, width, height);
    UIView *view = [[UIView alloc]initWithFrame:frame];

    CGFloat r, g ,b;
    r = (arc4random() % 10) / 10.0;
    g = (arc4random() % 10) / 10.0;
    b = (arc4random() % 10) / 10.0;
    [view setBackgroundColor:[UIColor colorWithRed:r green:g blue:b alpha:0.3]];

    //使用QuartzCore.framework替UIView加上圓角
    [view.layer setCornerRadius:15.0];

    [scrollView addSubview:view];
}

程式碼到此, UIScrollView 已經有捲頁的效果,只是它還無法與 UIPageControl 做溝通,不能將捲動的頁數設定在 UIPageControl 上,同樣地,操作 UIPageControl 來換頁,也無法反映於 UIScrollView 裡。(關於 UIView 圓角的製作請參考關於 UIView 圓角的二三事一文)


透過 UIScrollView 來設定 UIPageControl 的狀態
在這個部分我們將會實作 <UIScrollViewDelegate> 協定中的方法函式 scrollViewDidScroll:,來將目前 UIScrollView 所捲動的位移量換算成 UIPageControl 的頁數。
- (void)scrollViewDidScroll:(UIScrollView *)sender {
    CGFloat width = scrollView.frame.size.width;
    NSInteger currentPage = ((scrollView.contentOffset.x - width / 2) / width) + 1;
    [pageControl setCurrentPage:currentPage];
}


透過 UIPageControl 來設定 UIScrollView 的狀態
在這部分我們會透過一個自定義的方法函式 changeCurrentPage: 來將 UIPageControl 的頁數換算成 UIScrollView 的位移量,並且讓 UIScrollView 使用動畫的方式捲動過去,你可以將此方法函式與 UIPageControl 中的 Value Changed 事件做連結,這樣每當使用者操作 UIPageControl 改變頁數時,就會呼叫此函式並實作裡面的方法。
- (IBAction)changeCurrentPage:(UIPageControl *)sender {
    NSInteger page = pageControl.currentPage;

    CGFloat width, height;
    width = scrollView.frame.size.width;
    height = scrollView.frame.size.height;
    CGRect frame = CGRectMake(width*page, 0, width, height);

    [scrollView scrollRectToVisible:frame animated:YES];
}


ps:上述所使用的方式是一次產生所有的 SubView, 並將它們全部丟入 UIScrollView 中,這樣的方法雖然相當方便也容易理解,但是如果你的 UIScrollView  內容過於龐大複雜時,卻很容易占用過多的資源,一個比較好的方法是,建立一個 NSMutableArray 來存放這些 UIView 的 UIViewController,並且使用 [NSNull null] 來做暫時的初始化,等到 UIView  真的要顯示時才去產生它。





38 則留言:

  1. 學習了,感恩~

    回覆刪除
  2. 成功了 謝謝
    但有一個問題想問
    我想同時顯示3幅image
    我將scrollview 的size 於xib中拉闊至3倍後
    再將以上.m file的width都除3了
    現在成功同時顯示3個image
    但捲動時的step size卻很奇怪
    於第1頁scroll 一下就去到第4頁
    再scroll 一下去到第5貢
    想問捲動的size可以調較嗎?
    還是有其他方法解決以上問題?

    回覆刪除
    回覆
    1. Jacky 您好:

      從你設計的角度來看你把它拉長就可以在同一個UIView下顯示3張圖片,這點來看是沒有錯的。
      但是你在捲頁移動時所需要的移動量,並不需要改變,他應該要和你的 scrollview 寬度一樣。

      舉個簡單的例子,就像你在使用「延伸桌面的感覺依樣」,你有兩個同大小螢幕,螢幕的大小就是你的 scrollview 大小,當你想要呈現你右手邊的螢幕畫面時只要移動一個螢幕大小的寬度就可以了,也許你只是邏輯上有點打結。

      另外關於程式碼第4行 [pageControl setCurrentPage:currentPage] 這邊,只是運算上的問題而已,如果你顯示的畫面都正常,例如一開始你可以看到圖片 123,換到下一頁也可以看到圖片 456,那表示你的時做都沒有問題,再來我解釋一下為什麼是/2,因為我判斷是否成功切換到下一個畫面的依據是,當捲動的位移量超過寬度的一半時,所以這邊是除以二。

      有關您的提問,有關調整捲動的大小在程式碼第9行 [scrollView scrollRectToVisible:frame animated:YES], 參數 frame 就是位移量的大小。

      最後,希望你可以順利排除錯誤!

      刪除
  3. [pageControl setCurrentPage:0];
    設定為其它頁數似乎沒有反應...

    回覆刪除
    回覆
    1. cg2010studio 您好

      這只是設定起始數值,如果你一開始就要在其他的分頁畫面,要對 scrollView 做設定才對。

      刪除
  4. 建立一個 NSMutableArray 來存放這些 UIView 的 UIViewController,並且使用 [NSNull null] 來做暫時的初始化,等到 UIView 真的要顯示時才去產生它。

    我想問[NSNull null] 是怎樣用的?
    即是在PageControl的Delegate裡,計算Page的Frame,再調用這個Function ? ->[scrollView addSubview:view]~~
    需不需要前後都計算來避免有Delay ?

    回覆刪除
  5. WENNY BEN 您好:

    有關於 [NSNull null] 的問題,你可以前往 iOS Developer Library 下載他的示範程式碼,這邊的這記概念就是使用 [NSNull null] ,你可以在 PhoneContentController.m 中看到宣告一個 NSMutableArray 之後,先給予 [NSNull null] ,等到要使用時才去建立 UIViewController。
    http://developer.apple.com/library/ios/#samplecode/PageControl/Introduction/Intro.html

    希望對你有幫助!

    回覆刪除
  6. 你好,我想問可否給予其它設定變數參考?因我是新手,而且對此功能很感興趣。但找了很多資料也做不到。這是我的郵箱:ifong92@gmail.com.

    謝謝你!

    回覆刪除
    回覆
    1. Stanley Lo 您好:


      稍後會將demo寄給您!

      刪除
  7. 哈囉你好
    我最近在寫相關的程式,卻又因為是初學者 又是自學很多地方都不懂
    在.h的部分是不是還有些程式碼?

    不知是否可以請您寄demo給我 讓我參考
    我的信箱是:s98113210@stu.edu.tw 感謝您 謝謝

    回覆刪除
    回覆
    1. 謝至騏 您好:

      稍後會將完整的程式碼寄給您!

      刪除
  8. "建立一個 NSMutableArray 來存放這些 UIView 的 UIViewController,並且使用 [NSNull null] 來做暫時的初始化,等到 UIView 真的要顯示時才去產生它。 "
    請問有教學嗎?
    不好意思想要做可是還是不太知道要怎麼做:(

    回覆刪除
    回覆
    1. 您好:

      這邊沒有範例耶,因為作法實在非常多,當你先宣告 UIViewController 之後先別急著分配記憶體位置(alloc)或是初始化(init)它,這時候你就可以使用 NULL 來判斷這些尚未設定的 UIView,在進行初始化,這樣的好處在 ps 紅字部分也已經說明。

      我找了一了範例,你可以參考一下:
      http://www.edumobile.org/iphone/iphone-programming-tutorials/pagecontrol-example-in-iphone/


      使用這樣的結構來設計,也可以充分利用物件導向多型的特性,幫助你建立不同類型的 UIViewController。

      刪除
  9. 匿名7/10/2013

    您好
    我照著您的步驟做了
    但scrollView無法滑動,也沒跟pagecontrol一起切換
    請問哪邊出了問題

    回覆刪除
  10. 匿名7/10/2013

    不好意思附上我的步驟忘了說我的步驟
    將物件pagecontrol跟scrollView
    outlet到pageControl跟scrollView
    階有@property跟@synthesize
    委派跟framework都有弄好
    pagecontrol的valueChange也有outlet

    回覆刪除
    回覆
    1. 您好:

      你這樣說我還是不知道問題出在哪,scrollView無法滑動應該是你沒有把它的interaction打開,要啟用互動。

      不過也有可能是你沒有把view成功加入到scrollView中,變成內容一片空白你才會覺得他無法滑動。

      你可以設定中斷點看看上面這些函式是否有正確被觸發!

      刪除
  11. 匿名7/10/2013

    如果是CGRect frame = CGRectMake(width*i, 0, 100, 100);
    這樣的話就有5個view跑出來
    雖說有跑出來卻也無法滑動

    但若是照版大您的作法,在我試過後反而看不到
    interaction有打開了

    還是找不出問題出在哪><

    回覆刪除
    回覆
    1. 我剛剛想到一個,你是不是使用最新的xcode,回不會是AutoLayout的關係,你試試看把它關掉,下面的連結裡有關掉的方法
      http://furnacedigital.blogspot.tw/2012/10/uiview.html

      不過無法滑動,有可能你的scrollView上面又有新的View導致無法直接與scrollView做互動!

      刪除
    2. 匿名7/10/2013

      牛奶大太神了
      我把AutoLayout取消勾選run後
      終於可以滑動了^^

      感謝牛奶大的幫忙

      刪除
    3. 您好:

      不客氣,我自己之前也在AutoLayout這邊卡了很久︿︿;

      刪除
  12. 我剛剛照著您的文章做,還一知半解
    後來看到Apple的範例就恍然大悟了
    http://developer.apple.com/library/ios/#samplecode/PageControl/Introduction/Intro.html

    這邊補上大大漏說的部分

    ViewController.m檔的部分加上
    @interface ViewController ()
    {

    IBOutlet UIPageControl *pageControl;
    IBOutlet UIScrollView *scrollView;
    }

    @end

    然後在Interface Builder上
    各拉一個UIPageControl與UIScrollView

    將畫面上的UIPageControl
    拉關聯(Referencing Outlets) 至 pageControl
    另外拉Value Changed的Event 至
    - (IBAction)changeCurrentPage:(UIPageControl *)sender上

    將畫面上的UIScrollView
    拉關聯至 scrollView
    另外它的delegate要拉到File's Owner

    回覆刪除
    回覆
    1. 您好:

      謝謝您,不過我大部分的文章都沒有將元件與程式碼做結合,但在命名上也都竟量保留元件的名稱,不過還是非常感謝您的指點。

      如果想要了解元件與程式碼之間的運作,可以參考 「從使用 UIButton 說 Hello 開始說起(上)」
      http://furnacedigital.blogspot.tw/2011/03/uibutton-hello.html
      與「從使用 UIButton 說 Hello 開始說起(下)」
      http://furnacedigital.blogspot.tw/2011/03/uibutton-hello_11.html

      兩篇文章︿︿;

      刪除
  13. 你好,我是新手,在學純code開放pageview畫面切換的例子,請問能寄demo的程式碼給我參考嗎? chuangyeong@gmail.com,謝謝。

    回覆刪除
    回覆
    1. Yon 您好
      稍後會將完整的程式碼寄給您!

      刪除
  14. 謝謝您的教學,特別是Auto Layout的部份,受益良多!

    回覆刪除
    回覆
    1. 您好:
      新年快樂,不客氣!

      刪除
  15. 匿名1/14/2014

    我是一名新手,能將源程式發給我嗎??
    citedjade@gmail.com
    謝謝!!!

    回覆刪除
  16. 您好 想了解一下 Scrollview 和 pageController的切換,能否跟您取得範例程式碼
    謝謝
    tamd1980@gmail.com

    回覆刪除
    回覆
    1. 稍後會將完整的程式碼寄給您!

      刪除
  17. 跪求此demo code 謝謝 email: poseton1@gmail.com

    回覆刪除
    回覆
    1. 稍後會將完整的程式碼寄給您!

      刪除
  18. 可以跟您要一下code嗎? 謝謝

    Email:0801ctc@gmail.com

    我照著文章做了之後 跟我說這兩個找不到(showsHorizontalScrollIndicator &showsVerticalScrollIndicator )

    回覆刪除
    回覆
    1. 稍後會將完整的程式碼寄給您!

      刪除
  19. 匿名9/11/2016

    你好 我想問問 能用來轉VIEW嗎 我想學

    回覆刪除
    回覆
    1. 可以以唷 裡面就是放UIView

      刪除