[CS193P] 第八堂課摘要及心得筆記

在上次探討了Navigation Controller等內建的類別之後,在這一次的課程內容中,我們將看看要如何在iPhone開發平台上使用資料表格以及可以捲動的View,還請各位多多指教!
評論
評論

在上次探討了 Navigation Controller 等內建的類別之後,在這一次的課程內容中,我們將看看要如何在 iPhone 開發平台上使用資料表格以及可以捲動的 View,還請各位多多指教!

Scroll Views

在 iPhone 應用程式中,像是內建的地圖程式,我們可以自由的用手指在畫面上移動,而畫面也會隨之而捲動。而這樣的功能可以透過 UIScrollView 這個 UIKit 內建的 View 來完成。除了地圖之外,像是記事軟體中的文字顯示或是相簿的顯示也都是透過 UIScrollView 或其子類別完成的。此外,除了捲動之外,UIScrollView 也支援了放大縮小的功能,這將會在等會的內容中提到。

而建立一個 UIScrollView 的方法就如同其他 UIView 一般,我們可以透過 Interface Builder 或是在程式中完成:

    CGRect frame = CGRectMake(0, 0, 200, 200);     scrollView = [[UIScrollView alloc] initWithFrame:frame];

當然,我們也需要加入需要顯示的圖片作為 subView:

    frame = CGRectMake(0, 0, 500, 500);     myImageView = [[UIImageView alloc] initWithFrame:frame];     [scrollView addSubview:myImageView];

最關鍵的步驟是,我們需要替 UIScrollView 設定兩個屬性,分別是 contentSize 和 contentInsect,前者是將要顯示的 view 尺寸,後者則是顯示的 view 四周的留白,關於這兩者的差異,可以參考以下的示意圖:

那我們要如何得知使用者開始捲動畫面或是進行其他動作呢?我們可以使用 delegate,也就是讓某一個物件來負責接收這些訊息,並且做出對應的處理。在實做上,我們需要讓這個物件類別去實做<UIScrollViewDelegate> 這個 Protocol 中所需的方法以及一些選擇性的方法,像是下面這個:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {     // 對捲動的動作進行回應     if (scrollView.contentOffset ...) {     } }

至於放大縮小,我們則需要先設定:

scrollView.maximumZoomScale = 2.0; // 最大放大倍率 scrollView.minimumZoomScale = scrollView.size.width /myImage.size.width; // 最小縮小倍率

然後還要實做這個 delegate 方法:

- (UIView *)viewForZoomingInScrollView:(UIView *)view {     return someViewThatWillBeScaled; // 回傳某些可以被放大縮小的 View }

另外,我們也可以直接呼叫函式 - (void)setZoomScale:(float)scale animated:(BOOL); 來直接放大縮小到某個特定的倍率,也可以用 - (void)zoomToRect:(CGRect)rect animated:(BOOL); 來放大縮小到某個特定的位置,UIKit 會自動幫我們找到最適合的縮放倍率以做顯示。

Table View

在 iPhone 中的大大小小程式都會看到 Table View 的出現,除了一般的表格資料呈現之外,設定的呈現也往往用 Table View,主要分成以下兩種:

而針對 UITableView,我們有一些特別的慣用術語,我們稱作每一行為 Cell,而許多 Cell 可以組成 Section,每個 Section 上下又分別有 Header 和 Footer,而許多個 Section 則組成了整個 Table,當然 Table 也有 Header 和 Footer,整體來說,如以下兩張圖所示:

在 Table View 中顯示資料

重點來了,我們要如何在 Table View 中顯示資料呢?一開始我們可能會猜測是否是直接設定一個陣列給 Table View,然而這樣做會造成所有的資料都必須一口氣讀入記憶體,對於效能將是很大的危害。所以在 Cocoa 中,我們使用的是一種有彈性的方法,藉由指定資料來源 (Data Source),並且實做以下的方法:

// 回傳 TableView 中的 section 數量,若不需要使用 section,則預設為 1 - (NSInteger)numberOfSectionsInTableView:(UITableView *)table; // 回傳某個 section 中所包含的 rows 數量 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; // 回傳某 section 某 row 的 cell,也就是資料欄位 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

至於這個 NSIndexPath 到底是什麼?其實他是一個特別用來在巢狀的資料結構中定位的類別,包含了 row 和 section 兩個 property 可以設定、使用。在投影片 69 頁中有個簡易的 Data Source 實做,大家可以參考。

然而,雖然動態產生 cell 回傳比起直接建立所有 cell 來說在效能上有所增進,但仍然會遇到一些瓶頸。事實上,這些 cell 在同一張 table 中往往可以重新利用,只需要修改內容就可以了,因此 UIKIt 提供了這樣的方法:- (UITableViewCell *)dequeueReusableCellWithIdentifier: (NSString *)identifier;,在應用上,當我們 init 一個 UITableViewCell 的同時需要設定 reuseIdentifier,這個參數將會替 cell 做標籤,未來我們就可以用這個 reuseIdentifier 去取得同樣的 cell 並重複使用。

在投影片的 72 頁到 79 頁之間也介紹了一些重新讀取資料的方法可以呼叫,有興趣的讀者還請自行參考。

Table View 的動作行為

就如同一般的 UIView 一般,當 cell 被使用者點選時,我們需要做出相對的動作,像是 push 一個新的 View 到 NavigationController 上等。要完成這樣的功能,我們可以實做 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 這個方法。

而我們也可以禁止使用者點選某些特定的 Row,只要實做 - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath,回傳 NSIndexPath 代表允許使用者點選,而回傳 nil 則代表不能點選。

UITableViewController

誠如我們上面所見,要建立一個 UITableView 除了要設定 Data Source 之外,還有許多方法需要實做。為了方便我們能夠快速開發,Cocoa 提供了一個 UITableViewController,這個特別的 ViewController 包含了一個 TableView,並且定義了以上大多的方法還有少數的實做,此外,這個 Controller 也會處理一些預設的行為,像是在 TableView 出現之前,會幫我們呼叫 -reloadData 這個方法等等。

Table View Cells

在 Data Source 那邊我們曾經透過 (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier; 這個方法來建立 Cell,想必大家有注意這裡有一個 UITableViewCellStyle 參數,下圖就是幾種常用的:

而對於每個 UITableViewCell,我們可以設定 :

  • cell.textLabel.text -- 主要的文字
  • cell.detailTextLabel.text -- 補充文字(灰色的文字或是藍色的文字)
  • cell.imageView.image -- 顯示的圖像

除此之外,也可以藉由 - (UITableViewCellAccessoryType)tableView:(UITableView *)table accessoryTypeForRowWithIndexPath:(NSIndexPath *)indexPath; 設定 Accessory Type,也就是每個 Cell 的小按鈕,如下圖所示:

除了使用這些現成的的 UITableViewCell 方法之外,我們也可以直接修改 cell.contentView,自行加入想要的 UIView 作為 subView。

結論

在這一次的心得筆記中,我們涵蓋了 iPhone 中最常見到的 Table View 顯示,相信大家應該很有收穫。在下一次的筆記中,我們將會進入資料的儲存以及讀取,還請讀者敬請期待!

參考資源