φ(.. )メモシテオコウ UITableViewCellをドラッグ&ドロップで移動する

UITableViewCellをドラッグ&ドロップで移動するのはどうやんだ?と思ったらそんなに面倒な事では無かったのでめも。

まずはデータ。
セクションの名前のarrayとセクション内のデータを保持するarrayを保持するarray。

@property (nonatomic, retain) NSArray *sections;
@property (nonatomic, retain) NSMutableArray *data;

-(void) printDataCountInSections;

@end

テストデータをviewDidLoad()で設定。ドラッグで移動するためには編集モードにしないといけないのでここでやってますが長押しイベントを受け取ってからでも良いかも。
self.dataにはNSMutableArrayをセット。

- (void)viewDidLoad
{
    [super viewDidLoad];

    // セルの移動するためにsetEditingにYESを渡して編集状態にする
    [self.tableView setEditing:YES animated:YES];
    
    self.sections = [NSArray arrayWithObjects:@"section1", @"section2", nil];
    NSMutableArray *section1Data = [[NSMutableArray alloc] init];
    NSMutableArray *section2Data = [[NSMutableArray alloc] init];
    
    for (int i = 0; i < 3; i++) {
        NSString *tmp = [NSString stringWithFormat:@"Cell%03d", i];
        [section1Data addObject:tmp];
    }
    
    self.data = [NSMutableArray arrayWithObjects:section1Data, section2Data, nil];
    
}

numberOfSectionsInTableViewは特に変わったことはなし。

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // セクション数を返す
    return [self.sections count];
}

numberOfRowsInSection()も特に変わったことはしてない。

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // セクションにあるデータ数を返す
    return [[self.data objectAtIndex:section] count];
}

セルを作るときは対象セクション用にセルを作るという当たり前の処理を。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // この値はstoryboardで設定した文字列
    static NSString *CellIdentifier = @"mycell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    
    // Configure the cell...
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
    
    // セルの設定対象になるセクションはindexPath.sectionで分かる
    NSMutableArray *arr = [self.data objectAtIndex:indexPath.section];
    
    cell.textLabel.text = [arr objectAtIndex:indexPath.row];
    
    return cell;
}

CellIdentifierに使っている名称はstoryboardのここと合わせる。
f:id:masami256:20131121182339p:plain

titleForHeaderInSection()でセクション名を出す。

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    // セクションヘッダー表示
    return [self.sections objectAtIndex:section];
}

セクションにデータが無い場合に見た目が寂しいので一応データが無いという文言を入れておく。

-(NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section
{
    if (self.tableView.isEditing) {
        NSMutableArray *arr = [self.data objectAtIndex:section];
        
        // セクションにデータが無いときだけ
        if ([arr count] == 0) {
            return @"No Data";
        }
    }
    // データがある場合はnilを返す
    // ドラッグによる移動処理が終わったらビューのリロードが必要
    return nil;
}

編集モードの見た目設定。

- (UITableViewCellEditingStyle) tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // セルが編集可能だと左側に削除アイコンが出るけど、それを表示させない
    return UITableViewCellEditingStyleNone;
}

-(BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
    // editingStyleForRowAtIndexPath()でアイコン表示を無くしたけど、アイコン分の空白が残っているので左寄せする
    return NO;
}

セルの移動を許可

-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    // セルの移動を許可
    return YES;
}

editingStyleForRowAtIndexPath()、shouldIndentWhileEditingRowAtIndexPath()、canMoveRowAtIndexPath()でこのような見た目に。
f:id:masami256:20131121182623p:plain
「三」みたいなマークをつかんでドラッグ&ドロップができる。

実際の移動処理はここで。今回はセクションをまたいでの移動できるようにしてます。

-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
    NSLog(@"moveRowAtIndexPath: from: section:%u row:%u-> to: section:%u row:%u",
          sourceIndexPath.section, sourceIndexPath.row,
          destinationIndexPath.section, destinationIndexPath.row);
    
    NSLog(@"Before");
    [self printDataCountInSections];
    
    NSMutableArray *toArray = [self.data objectAtIndex:destinationIndexPath.section];
    NSMutableArray *fromArray = [self.data objectAtIndex:sourceIndexPath.section];
    
    NSString *item = [fromArray objectAtIndex:sourceIndexPath.row];
    [fromArray removeObjectAtIndex:sourceIndexPath.row];

    if (destinationIndexPath.row >= toArray.count) {
        [toArray addObject: item];
    } else {
        [toArray insertObject: item atIndex:destinationIndexPath.row];
    }
    
    NSLog(@"After");
    [self printDataCountInSections];
    
    [self.tableView reloadData];
    
}