忍者ブログ

Home > > IB不使用&オープンソースなiPadアプリ(テキストエディタ)を作る 第3回:ファイル操作とNavigationController、TableView

[PR]

  • 2024-04-25

Share on Tumblr このエントリーをはてなブックマークに追加

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

Comments:

Trackback+Pingback:

Listed below are links to weblogs that reference
[PR] from Born Neet

Home > > IB不使用&オープンソースなiPadアプリ(テキストエディタ)を作る 第3回:ファイル操作とNavigationController、TableView

Home > Edhita > IB不使用&オープンソースなiPadアプリ(テキストエディタ)を作る 第3回:ファイル操作とNavigationController、TableView

IB不使用&オープンソースなiPadアプリ(テキストエディタ)を作る 第3回:ファイル操作とNavigationController、TableView

Share on Tumblr このエントリーをはてなブックマークに追加

第3回。 今回はTableVIew&NavigationControllerでのファイル操作、
TextViewでの内容表示などなどを実装します。
ようやくテキストエディタな機能がつくわけですね。

それでは早速始めます。

ちょっと規模も大きくなってきたのでソースをベタ貼りするのはやめて、
ポイントだけに絞ってます。
また、jQuery.Syntaxを使ってコードを見やすくしてみました。

参考

今回は結構いじったので、参考にさせていただいたサイトもたくさんあります。
ありがとうございました。
なお、参考書籍は第1回をご参照下さい。

素材

今回から、フォルダとファイルの画像を使用しています。
この素材はいつもお世話になっている、Soft * Accessory様にお借りしました。
ハイクオリティな上に非常に緩い使用条件で素晴らしいです。

1. ファイル・ディレクトリの作成・削除

NavigationControllerのToolbarにボタンを表示して、
ファイル・ディレクトリの作成機能を付けます。
また、削除はTableViewの編集機能で行ないます。

Toolbarの表示(EdhitaNavigationController.m)

NavigationControllerのサブクラス、EdhitaNavigationControllerで行ないます。
※ TableViewController側でやるとうまくいきませんでした。
initでtoolbarHiddenを設定するだけです。

- (id)init {
	if (self = [super init]) {
		self.toolbarHidden = NO;
		// sizeToFitしないとportraitで起動したとき、popover内で表示されない。
		[self.toolbar sizeToFit];
	}
	return self;
}

作成・編集ボタンの表示(RootViewController.m)

先程表示したToolbarにファイル・ディレクトリ作成ボタンを追加します。
これはTableViewのサブクラスであるRootViewControllerのinitで設定します。
※ NavigationControllerでやってもうまくいきません。

また編集ボタンはNavigationBarの右に表示します。
この仕組みは標準で提供されており、
rightButtonItemにeditButtonItemを設定するだけです。

		// 画像ボタンを2個作って、それぞれファイル・ディレクトリの作成用のボタンとする
		UIImage* fileImage = [UIImage imageNamed:@"file.png"];
		UIImage* dirImage = [UIImage imageNamed:@"dir.png"];
		images_ = [[NSArray arrayWithObjects:fileImage, dirImage, nil] retain];

		// 右寄せ
		UIBarButtonItem *space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
		
		UIBarButtonItem *newFile  = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"file_new.png"] style:UIBarButtonItemStyleBordered target:self action:@selector(newFileDidPush)];
		UIBarButtonItem *newDir  = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"dir_new.png"] style:UIBarButtonItemStyleBordered target:self action:@selector(newDirDidPush)];

		NSArray *items = [NSArray arrayWithObjects:space, newFile, newDir, nil];
		[self setToolbarItems:items];

		// 編集ボタンの表示(selfのeditButtonを設定してやるだけでいい)
		self.navigationItem.rightBarButtonItem = [self editButtonItem];

こうなります。
ボタンの表示

作成・削除処理(RootViewController.m)

Toolbarに追加したbuttonのactionに指定したメソッドでファイル・ディレクトリの作成を行ないます。

これはMSFileManagerの機能を使います。
削除は、呼び出しがボタン押下時ではなく、
TableViewを編集モードにしてCellが削除された時になります。

// 新しいファイルの作成
- (void)newFileDidPush {
	
	NSError *error;

	// 連番のファイル名を取得
	NSString *fileName = [self nextFileName:@"untitled file"];
	NSString *fileContents = @"testあいうえお";

	NSString *filePath = [path_ stringByAppendingPathComponent:fileName];
	[fileContents writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];

	[items_ addObject:fileName];
	[self.tableView reloadData];
}

// 新しいディレクトリの作成
- (void)newDirDidPush {

	NSError *error;
	
	// 連番のディレクトリ名を取得
	NSString *dirName = [self nextFileName:@"untitled folder"];

	NSString *dirPath = [path_ stringByAppendingPathComponent:dirName];
	[[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:NO attributes:nil error:&error];
	
	[items_ addObject:dirName];
	[self.tableView reloadData];
}

// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    
	// Cellが削除された時、ファイルとitemsからも削除する
    if (editingStyle == UITableViewCellEditingStyleDelete) {
		
		NSString *path = [path_ stringByAppendingPathComponent:[items_ objectAtIndex:indexPath.row]];
		NSError* error;
		
		[[NSFileManager defaultManager] removeItemAtPath:path error:&error];
		
		// 配列からも消さないと落ちる
		[items_ removeObjectAtIndex:indexPath.row];
		
        // Delete the row from the data source
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
    }   
/*
	else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }
*/
}

2. ファイルブラウジング

新規作成したディレクトリをTableViewからアクセスして、
ファイルをDetailViewControllerに追加したTextViewで表示します。

ディレクトリ アクセス

Cell選択時にディレクトリだった場合、新たにRootViewControllerを作成し、
NavigationControllerにPushします。

- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    /*
     When a row is selected, set the detail view controller's detail item to the item associated with the selected row.
     */
 //   detailViewController.detailItem = [NSString stringWithFormat:@"Row %d", indexPath.row];
	
	NSString *path = [path_ stringByAppendingPathComponent:[items_ objectAtIndex:indexPath.row]];

	BOOL isDir;

	[[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir];

	// ディレクトリだった場合、そのPathを設定したRootViewControllerを作成
	if (isDir) {		
		RootViewController *rootViewController = [[RootViewController alloc] initWithPath:path];
		// detailはrootがもつ必要ないんじゃ?(navあたりに持たせればいい)
		rootViewController.detailViewController = self.detailViewController;
		[self.navigationController pushViewController:rootViewController animated:YES];
	}

ファイル表示(RootViewController.m, DetailViewController.m)

選択されたのがファイルだった場合は、DetailViewControllerに追加したTextViewに表示します。

// RootViewController.m

	// ファイルだった場合はDetailに内容を表示
	else {
		detailViewController.path = path;		
	}
// DetailViewController.m

// pathプロパティが変化した時にTextViewの内容を変更する
- (void)setPath:(NSString *)path {
	
	[self saveContents];

	path_ = [path retain];
	NSError *error;
	textView_.text = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error];
}

こうなります。
説明は省略しましたが、アイコンもつけています。
ファイルブラウズ

3. ファイル情報の表示・名前の変更

最後におまけ的な機能ですが、ファイル更新日・サイズの表示と、
ファイル名変更機能を追加します。

ファイル情報画面の呼び出し(RootViewController.m)

ファイル情報画面の表示はCellのアクセサリボタンのタップで行ないます。

// アクセサリボタンがタップされた時はファイル情報表示画面に遷移する
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {

	NSString *path = [path_ stringByAppendingPathComponent: [items_ objectAtIndex:indexPath.row]];
	EdhitaTableViewController *tableViewController = [[EdhitaTableViewController alloc] initWithPath:path];
	[self.navigationController pushViewController:tableViewController animated:YES];
	[tableViewController release];	
}

ファイル情報表示・名前変更(EdhitaTableViewController.m)

情報の表示とファイル名の変更は新たに作成した、
EdhitaTableViewControllerで実装します。
RenameはMSFileManagerのMove機能を使います。

// ファイル情報をCellに表示する。
// かなり汚いのでリファクタリング必要
// Customize the appearance of table view cells.
- (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];
		cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }

	// Configure the cell...
	cell.textLabel.text = [items_ objectAtIndex:indexPath.row];
	
	
	NSError *error;
	NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path_ error:&error];
	
	// switch内では変数宣言できないからif文の方が楽。
    if (indexPath.row == 0) {
		textField_ = [[UITextField alloc] initWithFrame:CGRectMake(cell.frame.size.width * 0.3, 0, cell.frame.size.width * 0.6, cell.frame.size.height)];
		textField_.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
		textField_.textAlignment = UITextAlignmentRight;
		textField_.delegate = self;
		textField_.text = [path_ lastPathComponent];
		textField_.returnKeyType = UIReturnKeyDone;
		textField_.clearButtonMode = UITextFieldViewModeWhileEditing;
		[textField_ becomeFirstResponder];		
		[cell.contentView addSubview:textField_];
	}
	else {
		UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(cell.frame.size.width * 0.3, 0, cell.frame.size.width * 0.6, cell.frame.size.height)];
		label.textAlignment = UITextAlignmentRight;
		label.backgroundColor = [UIColor clearColor];
		[cell.contentView addSubview:label];

		if(indexPath.row == 1) {
// timezoneが入ってくるので却下
//			textField.text = [[attributes objectForKey:NSFileModificationDate] description];
//			textField.text = [[attributes objectForKey:NSFileModificationDate] descriptionWithLocale:nil];

// documentに載ってるくせに。
//			textField.text = [[attributes objectForKey:NSFileModificationDate] descriptionWithCalendarFormat:@"%Y-%m-%d %H:%M:%S" timeZone:nil locale:nil];

			NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
			[dateFormatter setDateFormat:@"Y-MM-dd HH:mm:ss"];
			label.text = [dateFormatter stringFromDate:[attributes objectForKey:NSFileModificationDate]];						
		}
		else if(indexPath.row == 2) {
			label.text = [NSString stringWithFormat:@"%@ bytes", [attributes objectForKey:NSFileSize]];
		}
	}

    return cell;
}


// ファイル名変更(textField編集完了時と、Viewの非表示化の際に呼ばれる)
- (void)renameFile {
	NSError *error;
	NSString *dstPath = [[path_ stringByDeletingLastPathComponent] stringByAppendingPathComponent:textField_.text];
	[[NSFileManager defaultManager] moveItemAtPath:path_ toPath:dstPath error:&error];
}

完成

できました。 ファイル情報

[まとめ]

これで最低限のテキストエディタ機能はできました。
次回はいよいよSyntax Highlightです。
(まだどうやればいいか、検討もついてませんが。)

その前に、細かいバグ潰しをやらないとなぁ…。

[コード]

今回のコードは「Edhita3.zip」に入っています。
GitHubのdownloadページからダウンロードして下さい。

Downloads for tnantoka's Edhita - GitHub

PR

Comments:0

Comment Form

Home > Edhita > IB不使用&オープンソースなiPadアプリ(テキストエディタ)を作る 第3回:ファイル操作とNavigationController、TableView

Search
Loading
Feeds
Links
スポンサードリンク

Page Top