Apollo Grace
Apollo Grace

Reputation: 371

How to let the user reorder sections in a UITableView

I'm working on an app with stocks, arranged in portfolios. So this is a natural fit for the table view, and I'm working on the editing interaction; it's straightforward enough to allow the user to add or delete stocks, drag them around within one portfolio or to another portfolio, but one thing that I haven't been able to do gracefully is let the user drag one portfolio above or below another.

I've got a hacky solution right now, where row 0 of each section is the portfolio name, and if they drag that row above another portfolio, the whole table is reloaded with the portfolios switched. This works, but doesn't feel very natural.

I'm sure I'm not the first to encounter this problem; anyone have a more refined solution?

A related question - how do I let users create a new portfolio/section?

Upvotes: 10

Views: 3645

Answers (1)

michaelsnowden
michaelsnowden

Reputation: 6202

Easy peasy:

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController
{
    NSMutableArray *_data;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    _data = [NSMutableArray arrayWithObjects:@"One", @"Two", @"Three", nil];
    self.tableView.editing = YES;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _data.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"reuseIdentifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                      reuseIdentifier:identifier];
    }
    cell.textLabel.text = _data[indexPath.row];
    cell.showsReorderControl = YES;

    return cell;
}

- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return UITableViewCellEditingStyleNone;
}

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

- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
    [_data exchangeObjectAtIndex:sourceIndexPath.row withObjectAtIndex:destinationIndexPath.row];
}

@end

EDIT:

What you've asked for now is a little more complicated. I created an example that puts tables into cells, which gives you nested cells. The example is highly unattractive, but it works, and there's no reason you can't make it look pretty, so check it out:

https://github.com/MichaelSnowden/TableViewInCell

If that doesn't work for you, try making UITableView moveSection:(NSInteger) toSection:(NSInteger) look pretty. Documentation for that method is here.

My experience with the above method was that it's very easy to use, and it looks nice when it's called. A smart way to use it would be to create headers with tap gesture recognizers. On the first tap, highlight that section and record that indexPath, and on the second tap, call the method on the two index paths. It should work nicely, but you won't get drag-and-drop from it.

Upvotes: 3

Related Questions