目的
自分のデータとテーブル項目の並べ替えの連動を学習する。
主要クラス
UITableViewController、NSMutableArray
UITableViewControllerを継承したクラスを使い、自分が定義した独自クラスのインスタンス(以後MyClassと呼ぶ)をNSMutableArrayで管理しテーブル画面に表示する。
画面上での項目ドラッグによる並べ替えに連動させNSMutableArray内のMyClassインスタンスも並べ替える。
本ドリルではMyClassインスタンス配列の管理にNSMutableArrayを使っているが、好み、適正によって別のクラスを使ってもかまわない。
プロジェクトの名称
Table_Navi
サンプル実装説明
テーブルとナビゲーションを理解する(2)で作成したプロジェクトをそのまま流用し、並べ替え機能を追加する。
まず、ドラッグによる項目並べ替えをサポートするにはRootViewControllerでコメントアウトされている以下のメソッドを有効にすればよい。
- (void)tableView:(UITableView *)tableView
moveRowAtIndexPath:(NSIndexPath *)fromIndexPath
toIndexPath:(NSIndexPath *)toIndexPath {
}
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;
}
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;
}
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];
}
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のオブジェクト名が、ドリルダウン先のナビゲーションバーに表示される。
実際、項目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されたので、こちらは解放する
}
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を呼んでおくことを忘れずに。
これでドリルダウン先の表示も正しくなる。
プロジェクト