Fork me on GitHub

Programming Design Notes

UITableViewController 的簡單應用

| Comments

iPhone 裡經常也會看到 表格 (Table) 元件,10 個 非遊戲的應用程式,我想應該有 8 個也會使用到 UITableViewController 這個元件,是 iPhone 的重要元件之一。使用 Table 的原因之一可能是有很多相同特性的資料,但顯示的地方又不夠,所以借助表格來顯示大量且相同特性的資料,最經常用到的聯絡人應用程式或 iPod 應用程式,這 2 個也是使用表格的來顯示資料。

其實 UITableViewController 的用法不複雜,我發覺網上很多例子也會配合 Interface Builder,對初學者來說 Interface Builder 有點複雜,一時之間根本搞不清 XIBClass 的關係,所以我以下的例子不會用 Interface Builder去製作表格應用程式。

那麼現在先新增一個 Window-based Application,並命名為 Table


完成後新增一個 UIViewController subclass,並選取 UITableViewController subclass不用選取 With XIB for user interface,因為不會用到 Interface Builder,命名這個 Class 的名稱為 MyTable



新增完成後暫時不用理它,我們去打開 TableAppDelegate.h,加入一個 UINavigationController 去控制 UIViewController 之間的轉換,將原本的內容換成以下內容:
#import <UIKit/UIKit.h>
#import "MyTableController.h"

@interface TableAppDelegate : NSObject <uiapplicationdelegate> {
UIWindow *window;
UINavigationController *navigationController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@end

儲存並關閉 TableAppDelegate.h,然後打開 TableAppDelegate.m 並修改一下 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 這個 Method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    

// Override point for customization after application launch.
UITableViewController *tableViewController = [[MyTableController alloc] initWithStyle:UITableViewCellStyleDefault];

navigationController = [[UINavigationController alloc] initWithRootViewController:tableViewController];

[navigationController.navigationBar setBarStyle:UIBarStyleBlack];

[window addSubview:navigationController.view];

[window makeKeyAndVisible];

return YES;
}

程式碼第 4 行是創造出剛才新增的 MyTableController 的實例。而表格風格有以下這些:

  • UITableViewCellStyleDefault
  • UITableViewCellStyleValue1
  • UITableViewCellStyleValue2
  • UITableViewCellStyleSubtitle

聯絡人應用程式是 UITableViewCellStyleDefault 風格,而 iPhone 設定 是使用 UITableViewCellStyleValue1 的風格,另外 2 款沒見過有應用程式使用。

程式碼第 6 行是創做 UINavigationController 的實例並將 MyTableController 設為頂端的 ViewController
程式碼第 8 行是將頂上的導覽列轉為黑色,只是我覺得比較好看,不轉也沒問題。
程式碼第 10 行是將 navigationController 的頂端的 UIViewController 顯示出來。

現在完成了基本顯示,但還未可以運行,因為 MyTableController 內的程式碼有錯誤。

現在打開 MyTableController.h,新禎一個陣列去儲存資料,將以下程式碼複製並取化現有內容:
#import <UIKit/UIKit.h>

@interface MyTableController : UITableViewController {
NSMutableArray *data;
}

@end

儲存並關閉 MyTableController.h,然後打開 MyTableController.m 並修改 - (void)viewDidLoad 這個 Method:
- (void)viewDidLoad {
[super viewDidLoad];

self.title = @"Table";

//Create a data source
data = [[NSMutableArray alloc] init];

for (int i = 65; i <= 90; i++){

//Create a section
NSMutableArray *section = [[NSMutableArray alloc] init];

//Section Header
NSString *sectionName = [NSString stringWithFormat:@"%c", i];
[section addObject:sectionName];

for (int j = 1; j <= (rand() % 10) + 1; j++){

//Row Content
NSString *row = [NSString stringWithFormat:@"I am %c - %i", i, j];
[section addObject:row];

}

[data addObject:section];
[section release];

}
}

因為我沒有大量資料,所以自己動手去制作一堆資料並在表格載入時自動去創造資料。
我使用了 2D 陣列的原因是表格可以將資料分類,例如: A 字開頭的一類,B 字開頭的又另一類,這樣可以令使用者更容易找到他們想要的資料,下面會提到分類還有一個好處。

以上程式碼創造的資料會分類為 A - Z,每一個類別也會隨機創造出 1 到 10 個資料 (0 號索引是放類別名稱),現在有了資料便可以去修改一下錯誤的程式碼,將 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 修改為以下內容:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return [data count];
}

以上的內容是返回一個資料的類別數目給 UITableViewController 使用。返回的數值一定要大於 0

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 亦要修改一下:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return [[data objectAtIndex:section] count] - 1;
}

以上的程式碼會返回特定類別內的資料數目給 UITableViewController 使用。因為 0 號索引是放類別名稱,而索引不包進資料數量內,所以總資料數量減去 1 。返回的數值也一定要大於 0

以下的 Method 是 XCode 沒有預先做好的,需要自己加上:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [[data objectAtIndex:section] objectAtIndex:0];
}

以上的程式碼會返回特定類別內容給 UITableViewController 顯示出來。

那資料怎麼顯示出來? 表格是由很多 UITableViewCell 所組成,而資料將會放到到 UITableViewCell 內顯示出來,UITableViewCell 的樣子可以自己定的,因為這篇是簡單使用方法,或者遲一點寫一篇如何改變 UITableViewCell 的樣子。現在你可以籍由以下的 Method 去將資料夜到 UITableViewCell 內。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}

// Configure the cell...
NSInteger section = [indexPath section];
NSInteger row = [indexPath row];
[cell.textLabel setText:[[data objectAtIndex:section] objectAtIndex:row + 1]];
return cell;
}

第 11 行開始的才是的加入的程式碼,首先由 indexPath 抽取出 sectionrow,section 是一個數字,目的是令程式知道現在是第幾個類別,好讓我們令夠正確取得資料,而 row 則是第幾行的資料,請緊記這不是 A - Z 的資料的第幾行,面是現在的類別中的第幾行資料。正確拿到資料後便可以幾資料放到 UITableViewCell 內的 textLabel。最後返回 UITableViewCellUITableViewController 顯示出來。

現在可以運行程式,應該會像以下的樣子:


但內置的聯絡人應用程式旁邊有一個類別的列表,那又是怎麼做的? 其實很簡單,只需將所有類別放在一個陣列內並返回給 UITableViewController 使用就可以,你需要手動新增以下 Method:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView{
NSMutableArray *index = [[NSMutableArray alloc] init];
for (int i = 0; i < [data count]; i++){
[index addObject:[[data objectAtIndex:i] objectAtIndex:0]];
}
return index;
}

會變成以下樣子:

現在顯示資料沒有問題了,但想按下那筆資料便可以看詳細內容。首先新增一個 UIViewController subclass 命名為 DetailViewController


打開 DetailViewController.h 加入以下內容:
#import <UIKit/UIKit.h>

@interface DetailViewController : UIViewController {
NSString *data;
}

@property (retain, nonatomic) NSString *data;

@end

現在打開 DetailViewController.m,請將以下內容加到 @implementation DetailViewController 下一行:
@implementation DetailViewController

@synthesize data;

//.....source code

- (void)viewDidLoad Method 改成以下這樣來顯示資料:
- (void)viewDidLoad {
[super viewDidLoad];

self.title = data;

UIView *rootView = [[UIView alloc] init];
self.view = rootView;

UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
label.text = data;

[self.view addSubview:label];
[label release];
}

現在回到 MyTableController.h 去 import DetailViewController.h 令到 MyTableController 可以使用到 DetailViewController:

#import <UIKit/UIKit.h>
#import "DetailViewController.h"

@interface MyTableController : UITableViewController {
NSMutableArray *data;
}

@end

儲存後打開 MyTableController.m 作出少量修改,將 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 加入以下程式碼:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.

NSInteger section = [indexPath section];
NSInteger row = [indexPath row];

DetailViewController *detailViewController = [[DetailViewController alloc] init];

detailViewController.data = [[data objectAtIndex:section] objectAtIndex:row + 1];
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];

}

以上的 Method 目的是要知道使用者現在選取了那一項資料,再將資料放到 DetailViewController 內顯示出來。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 也要作出修改,令 UITableViewCell 顯示一個箭頭,提示用戶按下資料看詳細內容 (雖然我沒有加入真的詳細內容)。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

static NSString *CellIdentifier = @"Cell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}

// Configure the cell...
NSInteger section = [indexPath section];
NSInteger row = [indexPath row];
[cell.textLabel setText:[[data objectAtIndex:section] objectAtIndex:row + 1]];
[cell setAccessoryType:UITableViewCellAccessoryDetailDisclosureButton];
return cell;
}

運行應用程式會像以下的樣子:


上面設定的 Accessory 一共有 4 種:

  • UITableViewCellAccessoryCheckmark
  • UITableViewCellAccessoryDetailDisclosureButton
  • UITableViewCellAccessoryDisclosureIndicator
  • UITableViewCellAccessoryNone

以下的是 UITableViewCellAccessoryCheckmark 樣式:

以下的是 UITableViewCellAccessoryDisclosureIndicator 樣式:


UITableViewCellAccessoryNone 即是不顯示任何 Accessory

今篇就說到這裡完結,要是有什麼問題可以留言給我。

範例下載: Table.zip

相關書籍: Iphone SDK 4 Advanced Programming--Advanced Development for Apple Iphone & iPod TouchBeginning iPhone SDK Programming with Objective-C (Wrox Programmer to Programmer)iPhone SDK Development (The Pragmatic Programmers)