UITableView!

今回はiPhoneの中でもよく使われるであろうUITableViewを使用してみたいと思います。
TableViewを使う際にはUITableViewとUITableViewControllerの二つの選択肢があるのですが
今回はUITableViewControllerを使用します。

UITableViewControllerはUITableViewよりも自動的にいろいろやってくれます。
例えばこんなこと。

  • テーブルが表示される際(viewWillAppear:)に、データのリロード、選択行の解除。
  • テーブルが表示された後(viewDidAppear:)に、スクロールバーの点滅。
  • ナビゲーションバーの編集/完了ボタンを押したときに編集/通常モードに移行。

こんなアプリが出来上がるはずですね。



さて今回もTableDemoという名前で「Windows-Based Application」で作成します。

そこでできたプロジェクト内のフォルダ「Classes」を選択し

「ファイル」→「新規ファイル」→「UIviewController subClass」を選択、TableDemoViewControllerと名前を付けて保存します。

いつも通り、Interface Builderは切ってくださいね。

Interface Buiderとの関係を断ち切る!

では、いつもどおり、ViewControllerから始めます。
でも、本の通り打ち込んだ感じでソースを読んでも少し分からないとこもあります。。。

iPhone SDK Application Development: Building Applications for the AppStore

iPhone SDK Application Development: Building Applications for the AppStore

TableDemoViewController.h

#import <UIKit/UIKit.h>

// TableViewを使うのでUIViewContorollerを使うのではなく、UITableViewControllerを使う
@interface TableDemoViewController : UITableViewController {
        // NSMUtableArrayはインスタンス作成後、可変なサイズの配列
        // 可変じゃないのはNSArray
	NSMutableArray *fileList;
}

-(void) startEditing;
-(void) stopEditing;
-(void) reload;

@end

TableDemoViewController.m

#import "TableDemoViewController.h"

@implementation TableDemoViewController

-(id) init{
	self = 	[super init];
	if(self != nil){
		[self reload];
		
                // navigation Barの右側に[Edit]ボタンを表示する。
		self.navigationItem.rightBarButtonItem
		= [[[UIBarButtonItem alloc]
		   initWithBarButtonSystemItem:UIBarButtonSystemItemEdit
		   target:self
			action:@selector(startEditing)] autorelease];
		
                // navigation Barの左側に[reload]ボタンを表示する。
		self.navigationItem.leftBarButtonItem
		= [[[UIBarButtonItem alloc]
		   initWithTitle:@"reload"
		   style:UIBarButtonItemStylePlain
		   target:self
			action:@selector(reload)] autorelease];
	}
	
	return self;
}

-(void)startEditing{
        // 編集モードにする
	[self.tableView setEditing:YES animated:YES];
	
        // navigation Barの右側に[Done]ボタンを表示する。
	self.navigationItem.rightBarButtonItem
	= [[[UIBarButtonItem alloc]
	   initWithBarButtonSystemItem:UIBarButtonSystemItemDone
	   target:self
		action:@selector(stopEditing)] autorelease];
}

-(void)stopEditing{
        // 編集モードをやめる
	[self.tableView setEditing:NO animated:YES];

	// navigation Barの右側に[Edit]ボタンを表示する。
	self.navigationItem.rightBarButtonItem
	= [[[UIBarButtonItem alloc]
		initWithBarButtonSystemItem:UIBarButtonSystemItemEdit
		target:self
		action:@selector(startEditing)] autorelease];
}

-(void) reload{
        // NSDirectoryEnumeratorはディレクトリ内のファイル名を取り出せるようにする
	NSDirectoryEnumerator *dirEnum;
	NSString *file;
	
	fileList = [[NSMutableArray alloc] init];

        // homeディレクトリ内のファイル名を格納する
	dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:NSHomeDirectory()];
	
        // ファイル名を次々に取り出す
	while((file = [dirEnum nextObject])){
		[fileList addObject:file];
	}
	
	[self.tableView reloadData];
}

// numberOfSectionsInTableViewはテーブル内のセクション数を返す。今回は1個
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView{
	return 1;
}

// numberOfRowsInSectionはセクション内の行数を返す。
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
	return [fileList count];
}

// cellForRowAtIndexPathはセルの中身を表示する(ここが少し理解があやふや。。。)
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
        // fileListの1行目を表示する?
	NSString *CellIdentifier = [fileList objectAtIndex:[indexPath indexAtPosition:1]];
	
        // セルを作成する
	UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
	
        // 以前つかったcellのデータがあるのか
	if(cell == nil){
                // 無ければ作成する
		cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
		cell.text = CellIdentifier;
		
		UIFont *font = [UIFont fontWithName:@"Courier" size:12.0];
		cell.font = font;
	}
	
	return cell;
}

// commitEditingStyleはEdit呼ばれた時に削除モードになるように呼ばれるメソッドっぽい?
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle) editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
	if(editingStyle == UITableViewCellEditingStyleDelete){
		UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
		
		for(int i=0; i < [fileList count]; i++){
			if([cell.text isEqualToString:[fileList objectAtIndex:i]]){
				[fileList removeObjectAtIndex:i];
			}
		}
		
		NSMutableArray *array = [[NSMutableArray alloc] init];
		[array addObject:indexPath];
		[self.tableView deleteRowsAtIndexPaths:array withRowAnimation:UITableViewRowAnimationTop];
		
	}
}

// didSelectRowAtIndexPathはセルがタップされたらコールされる
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
	UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
	
        // セルがタップされたらAlertの画面を出す
	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"File Selected"
						  message:[NSString stringWithFormat:@"You selected the File '%@'", cell.text]
						  delegate:nil 
						  cancelButtonTitle:nil
						  otherButtonTitles: @"OK", nil];
	
	[ alert show];
	
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // Return YES for supported orientations
    return YES;
}

- (void)loadView {
	[super loadView];
}

- (void)dealloc {
	[fileList release];
    [super dealloc];
}


@end

コメントを書いておきました。
cellForRowAtIndexPathとcommitEditingStyleが少しあやふや。。。
もう少し調べてみたいと思います。。。。

Delegateのソースは、今までと同様ViewControllerをインスタンス化してwindowにsubviewとして貼付けてるだけです。

TableDemoAppDelegate.h

#import <UIKit/UIKit.h>

@class TableDemoViewController;

@interface TableDemoAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
	TableDemoViewController *viewController;
	UINavigationController *navigationController;
}

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

@end

TableDemoAppDelegate.m

#import "TableDemoAppDelegate.h"
#import "TableDemoViewController.h"

@implementation TableDemoAppDelegate

@synthesize window;
@synthesize viewController;


- (void)applicationDidFinishLaunching:(UIApplication *)application {    
	window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
		
	viewController = [[TableDemoViewController alloc] init];
	navigationController = [[UINavigationController alloc] initWithRootViewController:viewController];
	
	[window addSubview:[navigationController view]];
    // Override point for customization after application launch
    [window makeKeyAndVisible];
}


- (void)dealloc {
    [window release];
    [super dealloc];
}

@end

テーブルで一気に準備しておかなきゃならないメソッドが増えました。。。
もちょっと理解は深めたいな〜