33

I have a UITableView in my iOS app that gets refreshed periodically. The user is also able to move the tableview rows at all times (the tableview is always in editing mode).

I want to stop the refresh timer when the user starts dragging a row and start it again when the row is dropped.

The last part should be easy with moveRowAtIndexPath, but how to get notified about drag start?

double-beep
  • 5,031
  • 17
  • 33
  • 41
Cornelius
  • 4,214
  • 3
  • 36
  • 55

4 Answers4

44

Your UITableViewDelegate will receive the following notifications in response to reordering actions:

- (void)tableView:(UITableView *)tableView willBeginReorderingRowAtIndexPath:(NSIndexPath *)indexPath;
- (void)tableView:(UITableView *)tableView didEndReorderingRowAtIndexPath:(NSIndexPath *)indexPath;
- (void)tableView:(UITableView *)tableView didCancelReorderingRowAtIndexPath:(NSIndexPath *)indexPath;
marcprux
  • 9,845
  • 3
  • 55
  • 72
  • I do not see these delegate methods declared in the UITableView.h interface. Can you please explain where we can find them? – imnk Sep 07 '12 at 11:21
  • 4
    @mprudhom I received the callbacks but they are undocumented. Do you have these delegate methods in a production app? Is this considered private API? – Torsten Oct 31 '12 at 11:42
  • These methods are indeed called on delegate of the UITableView. Since they are undocumented, Apple might just break them in future releases of iOS, but from iOS 5 and up to iOS 7, this still works. I have yet to find out whether Apple would actually allow an app with these methods implemented to be published on App Store (good luck to me, I guess), but I don't think there's a good reason not to: if I implement these 3 methods in my UIViewController, and they are not documented and they don't start with underscore or whatever, how are they different from my own methods? – Vlas Voloshin Nov 02 '13 at 10:54
  • 3
    Just as a followup: I should mention that my app that uses these three methods has just been approved by Apple, so we should probably consider them safe to use for purpose of improving user experience. – Vlas Voloshin Jan 18 '14 at 12:32
  • The methods are still called on iOS 8.3. – Guillaume Algis May 05 '15 at 08:47
  • 1
    Cannot get called in swift? – Vienta Jul 08 '15 at 10:10
  • 2
    Still getting called in iOS 9.2 and Swift 2 with `func tableView(tableView: UITableView, didEndReorderingRowAtIndexPath indexPath: NSIndexPath)`. Such a useful method, I wonder why they aren't documenting it. – Alexandre G. Feb 24 '16 at 13:40
  • can anyone confirm that it's called on swift 3 and iOS 11? – binsnoel May 15 '18 at 03:29
  • Is it still valid ? or is there any alternative? – Nininea May 30 '18 at 07:57
  • 7
    For anybody wondering. It's still called in iOS 12 with Swift 4 using the following signature: `@objc public func tableView(_: UITableView, willBeginReorderingRowAtIndexPath indexPath: IndexPath)` – Georg Aug 29 '18 at 13:46
  • 1
    Note that these methods won't get called on iOS 11 or later if you have a `dragDelegate` set. In that case you can use `tableView(_:dragSessionWillBegin:)` and `tableView(_:dragSessionDidEnd:)` to accomplish the same thing. You can leave these methods as a fallback for older iOS versions. – robotspacer Sep 08 '18 at 01:44
9

I ran into the same problem some time ago and didn't find a solution. While I started this answer with an explanation why it can't be done, I actually found out how it can be done! :-)

In short: You have to create a custom subclass of UITableViewCell. Override layoutSubviews to attach a UILongPressGestureRecognizer to UITableViewCellReorderControl. Define a protocol and use a delegate to inform whoever you want to about the dragging state.

CustomTableViewCell.h:

#import <UIKit/UIKit.h>

@protocol CustomTableViewCellDelegate;

@interface CustomTableViewCell : UITableViewCell {
}

@property (nonatomic, assign) id <CustomTableViewCellDelegate> delegate;

@end

@protocol CustomTableViewCellDelegate
- (void)CustomTableViewCell:(CustomTableViewCell *)cell isDragging:(BOOL)value;
@end

CustomTableViewCell.m:

#import "CustomTableViewCell.h"

@implementation CustomTableViewCell

@synthesize delegate = _delegate;

- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer {
    if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {
        [_delegate CustomTableViewCell:self isDragging:YES];    // Dragging started
    } else if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
        [_delegate CustomTableViewCell:self isDragging:NO];     // Dragging ended
    }
}

- (void)layoutSubviews {
    [super layoutSubviews];

    for (UIView *view in self.subviews) {
        if ([NSStringFromClass ([view class]) rangeOfString:@"ReorderControl"].location != NSNotFound) {    // UITableViewCellReorderControl
            if (view.gestureRecognizers.count == 0) {
                UILongPressGestureRecognizer *gesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)];
                gesture.cancelsTouchesInView    = NO;
                gesture.minimumPressDuration    = 0.150;
                [view addGestureRecognizer:gesture];
            }
        }
    }
}

@end

Be aware that while this code doesn't use any private APIs it still might stop working if Apple changes its internal implementation (i.e. by changing the classname of UITableViewCellReorderControl).

Andy Friese
  • 6,349
  • 3
  • 20
  • 17
  • Thanks Peter, will need to rethink if my current implementation is worth the risk of relying on the class name or if I better change the "tableview is always editing" paradigm so that I can stop updating it once the user taps editing. Seems more reliable, just not sure how to integrate that in my UI ;) – Cornelius Mar 28 '12 at 07:59
  • This was the accepted answer, changed it to the answer from @mprudhom since Apple added and documented new delegate methods. – Cornelius Mar 09 '15 at 16:05
  • 2
    @Cornelius Where did Apple added and documented the new delegate methods? I'm on iOS 8 and they are still private API. – Rudolf Adamkovič Mar 21 '15 at 12:57
3

Just I found a hack, since apple will reduce the alpha, we can use that i guess

@interface CustomTableViewCell () 
@property (nonatomic, assign) BOOL isDragging;
@end

-(void)draggingWillBegan
{
    //use a delegate method to inform tableview
}

-(void)draggingDidEnd
{
    //use a delegate method to inform tableview
}

- (void)layoutSubviews
{
    [super layoutSubviews];

    //Since apple wont provide en
    if(self.alpha <= 0.95 && !self.isDragging){
        self.isDragging = YES;
        [self draggingWillBegan];
    }

    if(self.alpha >= 0.95 && self.isDragging){
        self.isDragging = NO;
        [self draggingDidEnd];
    }
}
Chandan Shetty SP
  • 5,087
  • 6
  • 42
  • 63
  • this is the only thing that worked for me. in my case, the tableview was inside a card view that's also listening for gestures. the dragging of the tableview would be caught up with the card view gestures. I had to "turn off" the card view gestures when I detect "isDragging" in my tableviewcells and return the gestures when the cells are not being dragged. – binsnoel May 15 '18 at 08:05
0

this method gets called when you're done moving cells:

- (void) tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
blueether
  • 3,666
  • 4
  • 26
  • 32