UITableViewの使い方2:編集モード編

UITableViewの編集モードで行の挿入・削除・移動を行う方法の説明です。

English top page

UITableViewの編集モード

ナビゲーションバーなどに編集ボタンを用意すると、そのボタンを押すことでUITableViewを編集モードへ移行させることができます。 編集モードでは新しくセルを挿入したり、今あるセルを削除したり、セルの順番を入れ替えたりと様々な操作を行うことが出来ます。 ここではUITableViewの編集モードの動作設定に使われるメソッドを説明してゆきます。 UITableViewの使い方1と同様にUITableViewはInterface Builder側で作っていて、UITableViewのdataSourceやdelegateがUITableViewControllerに設定されているものとして話を進めます。 なお、UITableViewの使い方1で使ったTableTestプロジェクトを引き続いて利用します。コードの変更は全てRootViewControllerに対して行います。

ここで紹介する項目は以下のものです

  • ナビゲーションバーに編集ボタンを追加する
  • 編集時もセルの選択を有効にする
  • 編集モードか否かを判別する
  • セルの挿入を行えるようにする
  • セルの削除を行えるようにする
  • セルの移動を行えるようにする

ナビゲーションバーに編集ボタンを追加する

ナビゲーションバーに編集ボタンを追加するためのヒントはViewDidLoadにあります。UITableViewの使い方1でコメントはそのままにしておきましょうと言った2行です。 あの2行のうち、下の行のコメントを外せば編集ボタンがナビゲーションバーの右側に現れます。なおこのボタン、システム側で用意しているものなので、既に多言語対応になってます。 英語ならEdit、日本語なら編集…と各言語で表記が変わります。自分でローカライズ対応する必要はありません。 ただし、Resourcesのinfo.plist(この場合標準だとTableTest-info.plistという名前になっているはずです)のLocalization native development regionをJapanにするか、 ローカライズ対応言語の中に日本語を入れておくかの設定は必要です。 TableTestのRootViewController.mファイルでコメントを外して「ビルドして進行」させてみてください。ナビゲーションバーの右側に編集ボタンが見えるはずです。

- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"テーブル設定のテスト";
    self.navigationItem.rightBarButtonItem = self.editButtonItem;
}

編集時もセルの選択を有効にする

デフォルトのままだと、編集モード時にはセルを選択することができません。編集モード時にも選択を有効にする、 言い換えるとdidSelectRowAtIndexPath:にメッセージを送るようにするには、Interface Builderで以前Table ViewのStyleを選んだところ(Attributes Inspectorの上の部分)にある、 Allow Selection While Editingにチェックを入れます。 これで編集モードでも選択が有効になります。TableTestのRootViewController.xibを開いて、Table Viewを選び、Allow Selection While Editingにチェックを入れ、 変更を保存してから「ビルドして進行」してみてください。編集モードでもセルをタップすることで下の階層へ移動できるようになります。


編集モードか否かを判別する

UITableViewが編集モードか否かはUITableViewControllerのメソッドファイル内において、BOOL型の変数self.editingで調べることが出来ます。 編集モードになっていればYES、なっていなければNOになっています。 これで条件分岐させることで、ユーザー側の操作が同じ場合でも、編集モード時と通常モード時で異なる動作をさせることが可能になります。 後述するsetEditing:animated:メソッドなど、引数にBOOL型のeditingを持つものもあります。そのメソッド内ではそのeditingをself.editingの替わりに使います。


セルの挿入を行えるようにする

行数が固定ではなく、ユーザーの操作により行数に変化が起こる場合には、セルの挿入や削除を行う必要があります。 現在のTableTestはデータが固定状態にあるため、そのような行数変化を行わせるには向きません。 そこで設計を変更し、データをNSMutableArrayに格納するようにしましょう。 各行に表示するテキストをNSStringオブジェクトして格納します。 複雑にするとわかりにくいので、とりあえずはセクションを1個だけとし、1個のNSMutableArrayだけを使うことにします。 そのため、セクションは1個だけに変更します。 セクションの数を決めるのはnumberOfSectionsメソッドでした。これで返る値を1に変更します。

- (NSInteger)numberOfSections {
    return 1; // セクションは1個とします。
}

次にRootViewController.hの@interface内にNSMutableArray *型のメンバを追加し、@propertyも追加します。Xcode側でTable Viewを操作する必要もあるので、UITableView *型のメンバも追加します。具体的には以下のようなコードになります。変更を保存後に、Interface BuilderでmyTableViewをTable Viewへ紐付けしておいてください。繰り返しになるため紐付けについてここでは詳しく説明しません。

@interface RootViewController : UITableViewController {
    NSMutableArray *stringArray;
    UITableView *myTableView;
}
@property (nonatomic, retain) IBOutlet UITableView *myTableView;
@property (nonatomic, retain) NSMutableArray *stringArray;

@end

そしてRootViewController.mに@synthesizeとdeallocへのrelease追加を行います。

@implementation RootViewController
@synthesize stringArray;
@synthesize myTableView;

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

セクションの行数はstringArrayの中にあるオブジェクト数に従って変化するので、numberOfRowsInSection:メソッドを以下のように書き換えます。

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [stringArray count];
}

編集のときだけナビゲーションバーの左側に追加用のボタンを表示するようにします。そのためにsetEditing:anmated:メソッドを追加します。これはナビゲーションバーの編集ボタンを押すたびに実行されます。ボタンには押された時にRootViewController(self)にaddRow:メッセージを送るようにターゲット設定をしています。編集モードから戻った時に、追加のためのボタンは消えるようにしています。

- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
    [super setEditing:editing animated:animated];
    [myTableView setEditing:editing animated:YES];
    if (editing) { // 現在編集モードです。
        UIBarButtonItem *addButton = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd
                                     target:self action:@selector(addRow:)] autorelease];
        [self.navigationItem setLeftBarButtonItem:addButton animated:YES]; // 追加ボタンを表示します。
    } else { // 現在通常モードです。
        [self.navigationItem setLeftBarButtonItem:nil animated:YES]; // 追加ボタンを非表示にします。
    }
}

追加ボタンが押された時のメソッドを追加します。メソッド名はaddRow:としています。追加する行が毎回同じなのも味気ないので、挿入ごとに番号がかわるようにしてみます。そのためにaddCountというNSInteger型の変数を用意し、以下のようなコードにします。

- (void)addRow:(id)sender {
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:stringArray.count inSection:0];
    NSArray *indexPaths = [NSArray arrayWithObjects:indexPath,nil];
    [stringArray addObject:[NSString stringWithFormat:@"追加された行その%d",addCount]];
    addCount++; // 次に使うとき用にaddCountに1足しています。
    [myTableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationTop];
}

これで追加ボタンを押すと、「追加された行その~」と表示された行が追加されるようになります。この値はメソッドを抜けても消えないように、RootViewControllerが持つメンバ変数にします。そこでまずメソッドファイルの冒頭の部分に@synthesizeを追加します。

@implementation RootViewController
@synthesize stringArray;
@synthesize myTableView;
@synthesize addCount;

そしてヘッダに今の変数の宣言と@propertyを加えますが、ついでに先ほどのaddRow:メソッドの宣言も追加しておきましょう。

@interface RootViewController : UITableViewController {
    NSMutableArray *stringArray;
    UITableView *myTableView;
    NSInteger addCount;
}
@property (nonatomic, assign) NSInteger addCount;
@property (nonatomic, retain) IBOutlet UITableView *myTableView;
@property (nonatomic, retain) NSMutableArray *stringArray;

- (void)addRow:(id)sender;

@end

一応スタートの時点で何個かデータがある状態にしましょう。そのため、viewDidLoadでstringArrayを初期化して、3個のNSStringオブジェクトを入れておきます。ついでに先ほどのaddCountの初期値を1に設定します。for文を使ってiが1から3までの範囲で繰り返させてstringArrayにオブジェクトを追加しています。

- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"テーブル設定のテスト";
    self.navigationItem.rightBarButtonItem = self.editButtonItem;
    self.stringArray = [[NSMutableArray alloc]init];
    for(int i=1;i<4;i++) {
        [stringArray addObject:[NSString stringWithFormat:@"元からある行その%d",i]];
    }
    addCount = 1; // addCountは1からスタートします。
}

ここまででテーブルへの行の追加に関する仕掛けは完成しました。


セルの削除を行えるようにする

次に、編集モードで左側のマイナスボタンを押し、削除ボタンが押された時の反応を書きます。commitEditingStyle:forRowAtIndexPath:メソッドを以下のように変更します。

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
        forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        [stringArray removeObjectAtIndex:indexPath.row]; // 削除ボタンが押された行のデータを配列から削除します。
        [myTableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    } else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // ここは空のままでOKです。
    }
}

セルの移動を行えるようにする

さらにセルの順番を並べ替えられるようにしていみましょう。テンプレートから作った状態ではコメント化されているメソッドを使います。

- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
    return YES;
}

これで移動のためのマークが行の右側に出ます。あとは実際の動作を記述するmoveRowAtIndexPath:toIndexPath:メソッドを追加します。

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
    if(fromIndexPath.section == toIndexPath.section) { // 移動元と移動先は同じセクションです。
        if(stringArray && toIndexPath.row < [stringArray count]) {
            id item = [[stringArray objectAtIndex:fromIndexPath.row] retain]; // 移動対象を保持します。
            [stringArray removeObject:item]; // 配列から一度消します。
            [stringArray insertObject:item atIndex:toIndexPath.row]; // 保持しておいた対象を挿入します。
            [item release]; // itemは不要になるので開放します。
        }
    }
}

ここまでのことを反映させて「ビルドと進行」させるとこのようになります。 保存機能はないので再起動すると元(左のスクリーンショット)の状態に戻ります。

起動直後 編集の一例
前へ次へ
Copyright© 2009 Konton All rights reserved. - このサイトについて - サイトマップ

Valid XHTML 1.1 正当なCSSです!