目的
 自分のデータとテーブル項目の並べ替えの連動を学習する。


$テン*シー*シー-1

主要クラス
UITableViewController、NSMutableArray


 UITableViewControllerを継承したクラスを使い、自分が定義した独自クラスのインスタンス(以後MyClassと呼ぶ)をNSMutableArrayで管理しテーブル画面に表示する。
 画面上での項目ドラッグによる並べ替えに連動させNSMutableArray内のMyClassインスタンスも並べ替える。
 本ドリルではMyClassインスタンス配列の管理にNSMutableArrayを使っているが、好み、適正によって別のクラスを使ってもかまわない。


プロジェクトの名称
Table_Navi


サンプル実装説明

 テーブルとナビゲーションを理解する(2)で作成したプロジェクトをそのまま流用し、並べ替え機能を追加する。

 まず、ドラッグによる項目並べ替えをサポートするにはRootViewControllerでコメントアウトされている以下のメソッドを有効にすればよい。
 - (void)tableView:(UITableView *)tableView
moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
toIndexPath:(NSIndexPath *)toIndexPath {
}

 中身が何も無くても、実際に動かしてみると編集モードで項目が移動できる事が確認できるはず。
 一番上の項目が編集対象外なのもそのまま有効に機能する。
 ただし、第2項目は追加用の項目としているので、移動できるのはおかしい。これを止めるにはもう一つコメントアウトされているメソッドtableView:canMoveRowAtIndexPath:を有効にする必要がある。そしてこちらは第2項目が移動不可であることを返さなければならない。
- (BOOL)tableView:(UITableView *)tableView
canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 1) {
return NO;
}
return YES;
}

 これで第1項目、第2項目は移動はできなくなるが、他の項目が第1項目、第2項目にドラッグ可能。
 これを防止するのは以下のメソッドを加え、第1項目、第2項目の上に移動する事を許可しないようにする。
- (NSIndexPath *)tableView:(UITableView *)tableView
targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath
toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath {
if (proposedDestinationIndexPath.row < 2) {
return [NSIndexPath indexPathForRow:2 inSection:proposedDestinationIndexPath.section];
}
return proposedDestinationIndexPath;
}

 これで画面上は問題なく項目の移動がおこなわれるようになる。
 最後は内容部との連動。

 逆に言えば、現段階では内容部はいっさい変化していない。
 この事はtableView:didSelectRowAtIndexPath:に以下の処理を加える事で簡単に確認できる。
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:@"AnotherViewController" bundle:nil];
MyClass* m = [instanceArray objectAtIndex:indexPath.row];
anotherViewController.title = [m name];

[self.navigationController pushViewController:anotherViewController animated:YES];
[anotherViewController release];
}

 この実装により、タップした項目に対応するinstanceArrayのオブジェクト名が、ドリルダウン先のナビゲーションバーに表示される。

$テン*シー*シー-2

 実際、項目5を項目3の位置に移動させても、使われるのはあくまで項目3のinstanceArrayのオブジェクトとなっている。
 これは、画面上の移動がinstanceArray側に反映されていないから。
 以下のUITableViewDataSourceプロトコル側メソッドを実装する事で、instanceArrayの状態を画面上と連動させる。
- (void)tableView:(UITableView *)tableView
moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
toIndexPath:(NSIndexPath *)toIndexPath {
int toIndex = toIndexPath.row;
int fromIndex = fromIndexPath.row;
id obj = [instanceArray objectAtIndex:fromIndex];
[obj retain]; // removeObjectAtIndex時にreleaseが
// 呼ばれるから解放されないように一時的にretainする

[instanceArray removeObjectAtIndex:fromIndex];
[instanceArray insertObject:obj atIndex:toIndex];
[obj release]; // instanceArrayにretainされたので、こちらは解放する
}

 いったんfromIndexPath.rowのオブジェクトをinstanceArrayから取りはぶき、あらためてtoIndexPath.rowの位置に入れなおしている。このさいNSMutableArrayのremoveObjectAtIndex:メソッドは削除するオブジェクトに対してreleaseを発行するので、それにより解放される事を防ぐためretainを呼んでおくことを忘れずに。
 これでドリルダウン先の表示も正しくなる。

プロジェクト