Tutorial on How to drag and drop item from UITableView to UITableView [closed]

I've been banging my head on this one for a while and I figured it out. I want to give back to the community since I've gotten a lot of help from this website :).

I'm trying to copy an item from one UITableView to another UITableView and information I've seen on the web regarding how to do this has been sketchy at best. I figured it out on my own and so I'll describe my little architecture.

  • Master UIView
    • UIView with UITableView
      • Custom UITableViewCell
        • Custom UIView that gets copied (Person object in my case)
    • UIView with UITableView
      • Custom UITableViewCell
        • Custom UIView that gets copied (Person object in my case)

The person object that I have in the UITableView is the object that I want to drag and drop out of one table and into another. I had the most difficult figuring out how to pop the item out of the table and drag it over in a single smooth motion. For the longest time, it would take me two touches in order to perform the operation.

Starting with the Person object, this is a simple object that contains an image. I had to implement my own touchesMoved method to change the center position of the Person when a drag is taking place.

-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    if( m_moveable == YES ){
        UITouch *touch = [touches anyObject];
        CGPoint location = [touch locationInView:self.superview];

        if( 0 < location.x-50 && location.x+50 < 768 ){ 
            if( 0 < location.y-50 && location.y+150 < 1004 ){
                self.center = location;
            }
        }
    }
}

I set the Person object's userInteractionEnabled flag to NO on initialization so that any clicks in the table would not get caught by the Person object. The Person object in that case would move within the table which defeats the purpose.

The next object is my custom UITableViewCell. This object is responsible to catch the user's first touch. What it's supposed to do is catch this touch and "pop" the Person out. The Person is one of the subviews belonging to the custom UITableViewCell.

 - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UIView *parent = self.superview.superview.superview;    

    Person *s = nil;
    for( UIView *child in self.subviews ){
        if( [child isKindOfClass:[Person class]] ){
            s = child;
            s removeFromSuperview];
            break;
        }        
    }

    if( s != nil ){
        self.userInteractionEnabled = NO;
        s.userInteractionEnabled = YES;
        UITableView *subParent = self.superview;   //EDIT #1
        subParent.scrollEnabled = NO;              //EDIT #1

        [parent addSubview:s];
        //[self touchesEnded:touches withEvent:event]; //EDIT #1
    }
}

It's important to note the userInteractionEnabled flag getting flipped in the above method. Before the touch, the Person object is "off limits" to a person's touch. After the custom cell catches a move, the Person is released by adding it to the parent's view and then activated (userInteractionEnabled=YES). The Person object is then "born" and can handle move touches on it's own.

This has one minor glitch in that the Person object blinks in the upper left corner but then drops down immediately to the user's finger.

The final part of this design is that the master UIView needs to handle a "touch transition." When the user is touching the table and the Person object gets popped out, the app needs to realize that focus needs to be removed from the table and directed towards the Person object. The way that this was done is that the hitTest method in the master UIView was overloaded with the following.

- (UIView*) hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *rv = nil;
    for(UIView *child in self.subviews){
        if( [child isKindOfClass:[Person class]] ){
            rv = child;
            child.center = point;
            break;
        }
    }
    if( rv == nil ){
        rv = [super hitTest:point withEvent:event];
    }   
    return rv;
}

The way that this code works, is that when the Person is popped out of the table, it doesn't have a touch focused at it. The touch is "owned" by the UITableView from which the Person was popped out of. The hitTest method is the key to refocusing that touch. Regularly, the system checks to see which UIView is the focus of a touch. The hitTest method is called by the system to identify that UIView. When the Person is attached to the master view, this hitTest function iterates through all subviews and detects the presence of the Person and returns it as the "dominant" touched object. Any movement of your finger will immediately be reported to the Person and not to the UITableView.

This is guts of the implementation. To have a UITableView "catch" the moving object is simple now and I'll leave it for you to try! If you have any questions, please post them!

EDIT #1 Dropping the Person object is proving harder than I thought :). I had to add a line to prevent the UITableView from scrolling when the parent is being moved around because the UITableView is sucking in all the movement events.
The touchesEnded function fires in the custom UITableViewCell class.
mj


Hi I've managed to create drag&drop of a row in a UITableView onto another row of the same table. I created a uiImageView representing the moving item (which was not removed from the table) and attached it to the frontmost UIView to made it draggable on the whole screen. Here are some post about the pains I faced:

  1. UITableView: Drag a row onto another one
  2. UITableView: scrolling programmatically the content view
  3. UITableView: custom gestures make it scrolling no more

Hope this can help ^^