views:

74

answers:

2

Is there a way to increase the speed that you can drag a cell up/down during a table's movable row mode of a UITableView? For instance, there seems to be a standard speed that the table will allow you to drag the cell when you are moving it around and the scroll speed seems to increase if you hold it near the top/bottom edge of the device screen. For a given cell height and a whole bunch of cells, this might take a while to re-order them all if I have to use their standard slow scrolling.

So basically, I would like to increase the speed that it scrolls up/down when you are dragging the cell so it won't take as long to get to where you want to drop the cell in place.

I understand that you can speed up the moving process by decreasing cell height to place more cells on the device screen, but I'd rather do this only if I can't increasing scrolling speed.

Any suggestions or ideas from past experiences with this? Any advice is greatly appreciated!

+2  A: 

If the list is going to get that long, you may want to consider other reordering mechanisms. For example, my Netflix queue includes a number in each call specifying the order of the queue, 1 for the movie about to ship, 2 for the next and so on to 187 or so for the most recent additions to the queue. I can drag an entry to reorder like on the iPhone, but I can also change the order numbers in the cells to reorder the entries, which is much easer than having to drag #187 to the 10th spot in the queue. There is also a button in each entry to put that entry on the very top.

In your app, you can add extra controls in edit view to assist in reordering such as the order number or "up/down 10" buttons.

If there are common reasons your users would want to order entries, you can have a button that handles that, such as "move up to next nearest blue entry."

John Franklin
Thank you for the answer, but unfortunately I cannot use these techniques due to the random nature of the data.
iWasRobbed
+2  A: 

Here you go. A proof of concept of a speed enhancer of the scroll when you move a cell, SDK 3.1. It may not pass Apple's requirements since overriding _beginReorderingForCell and _endReorderingForCell looks a little off-spec. There are other ways to determine if a cell starts or ends reordering (e.g. subclassing UITableViewCell and finding some measure) but this is the easiest I think.

The approach is quite simple: for every movement of Y pixels down, we move 2*Y pixels down, only when reordering.

The problem is that the currently dragged cell is a subview of the table view, so it shifts with the table view if we move it. If we are to correct for that within this setContentOffset, it has no effect since the position of the cell will be set based on values calculated apart from the current contentOffset. Therefore we correct an instant later using performSelector.

I left the debugging lines in there for convenience. All you need to do is to use FastUITableView instead of UITableView (esp. in you NIB)

You may of course want to add some timing things, so that the speed only goes up after 1 second or so. That will be trivial.

@interface FastUITableView : UITableView
{
    UITableViewCell *draggingCell;
    CGFloat lastY;
}
@end


@implementation FastUITableView

-(void)_beginReorderingForCell:(UITableViewCell*)cell
{
    printf("begin reordering for cell %x\n",cell);
    draggingCell = cell;
    lastY = -1.0f;
    [super _beginReorderingForCell:cell];
}

-(void)_endReorderingForCell:(UITableViewCell*)cell wasCancelled:(BOOL)cancelled animated:(BOOL)animated
{
    printf("end reordering for cell %x\n",cell);
    draggingCell = nil;
    [super _endReorderingForCell:cell wasCancelled:cancelled animated:animated];
}

-(void)setContentOffset:(CGPoint)pt
{
    if ( !draggingCell )
    {
        [super setContentOffset:pt];
        return;
    }

    if ( lastY < 0 ) lastY = pt.y;
    CGPoint fast = pt;
    float diff = pt.y - lastY;
    //diff *= 0.5; /// <<--- control speed here
    fast.y = pt.y + diff;

    if ( fast.y > self.contentSize.height - self.superview.frame.size.height )
    {
        CGFloat corr = fast.y - self.contentSize.height + self.superview.frame.size.height;
        printf("Correction: %f\n",corr);
        fast.y -= corr;
        diff -= corr;
    } else if ( fast.y < 0.0f ) {
        CGFloat corr = -fast.y;
        printf("Correction: %f\n",corr);
        diff += corr;
        fast.y += corr;
    }

    [self performSelector:@selector(moveCell:) withObject:[NSNumber numberWithFloat:diff] afterDelay:0.01];

    lastY = fast.y;

//  printf("setting content offset: %f -> %f of max %f\n",pt.y, fast.y, self.contentSize.height);
    [super setContentOffset:fast];
}

-(void)moveCell:(NSNumber*)diff
{
    CGRect frame = draggingCell.frame;
    frame.origin.y += [diff floatValue];
//  printf("shifting cell %x with %f\n",draggingCell,[diff floatValue]);
    draggingCell.frame = frame;
}
mvds
A bit jittery, but it works and an interesting way of doing this. I'll have tweak it some and also find a way around using `_beginReorderingForCell` and `_endReorderingForCell`. Thanks for the help!
iWasRobbed
Thanks, first bounty! My experience (<3GS phone) is that the animations just can't keep up with scrolling, but that may be a perfect visual cue for the user. I though of another approach to maybe fix this: make the UITableView extend 100 px above and 100px below the screen, so the iphone starts rendering them before they need to be displayed (you know, now it has to "dequeueCellFromWhatever" every time, which may lead to some lag)
mvds
of course then you have to detect touching the edges yourself, but that may not even be a bad thing.
mvds
as for other means of dragging detection, it seems you get 2 identical `setContentOffset` calls before dragging starts (ignore the 2 identical 0.0 `setContentOffset`s at the start) then the *next* `UITableViewCell` to move is the dragging cell... Otherwise maybe try to get your hands on `UITableViewCellReorderControl` (but I suggest first trying to slip the current solution past apple)
mvds