I\'m wondering if anyone has a link to a good tutorial or can point me in the right direction to recreate the \'drag to reorder\' cells in a UITableView like Epic Win App. The
The simplest way, using the built-in methods, is as follows:
First, set your table cell to have shows reorder control on. Simplest example (using ARC):
This is assuming NSMutableArray *things
has been created somewhere else in this class
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"do not leak me"];
if (!cell) {
cell = [[UITableViewCell alloc] init];
cell.showsReorderControl = YES;
}
cell.textLabel.text = [things objectAtIndex:indexPath.row];
return cell;
}
Implement your two UITableViewDelegate
methods, like this:
This method tells the tableView it is allowed to be reordered
-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
This is where the tableView reordering actually happens
-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath {
id thing = [things objectAtIndex:sourceIndexPath.row];
[things removeObjectAtIndex:sourceIndexPath.row];
[things insertObject:thing atIndex:destinationIndexPath.row];
}
and then somehow, somewhere, set editing to true. This is one example of how you can do this. The tableView must be in editing mode in order for it to be reordered.
- (IBAction)doSomething:(id)sender {
self.table.editing = YES;
}
Hope that serves as a concrete example.
This solution here will function similar to the Reminders app on OSX which allows you to reorder cells by holding and dragging anywhere.
I've created a subclass of UITableView that allows you to drag and reorder the cells without having to have editing
on or use the standard reorder handles.
The basic idea is I use the touches began and touches ended to get which cell was tapped, I then create a fake view that is really just a screenshot of the view you tapped and move it back and forth, swapping the cells in the background. When the user lets go I then unhide the original view.
class ReorderTableView: UITableView {
var customView:UIImageView?
var oldIndexPath:NSIndexPath?
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let touch1 = touches.first else{
return
}
oldIndexPath = self.indexPathForRowAtPoint(touch1.locationInView(self))
guard (oldIndexPath != nil) else{
return
}
let oldCell = self.cellForRowAtIndexPath(self.oldIndexPath!)
customView = UIImageView(frame: CGRectMake(0, touch1.locationInView(self).y - 20, self.frame.width, 40))
customView?.image = screenShotView(oldCell!)
customView?.layer.shadowColor = UIColor.blackColor().CGColor
customView?.layer.shadowOpacity = 0.5
customView?.layer.shadowOffset = CGSizeMake(1, 1)
self.addSubview(customView!)
oldCell?.alpha = 0
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let touch1 = touches.first else{
return
}
let newIndexPath = self.indexPathForRowAtPoint(touch1.locationInView(self))
guard newIndexPath != nil else{
return
}
guard oldIndexPath != nil else{
return
}
if newIndexPath != oldIndexPath{
self.moveRowAtIndexPath(oldIndexPath!, toIndexPath: newIndexPath!)
oldIndexPath = newIndexPath
self.cellForRowAtIndexPath(self.oldIndexPath!)!.alpha = 0
}
self.customView!.frame.origin = CGPointMake(0, touch1.locationInView(self).y - 20)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.customView?.removeFromSuperview()
self.customView = nil
guard (oldIndexPath != nil) else{
return
}
self.cellForRowAtIndexPath(self.oldIndexPath!)!.alpha = 1
}
func screenShotView(view: UIView) -> UIImage? {
let rect = view.bounds
UIGraphicsBeginImageContextWithOptions(rect.size,true,0.0)
let context = UIGraphicsGetCurrentContext()
CGContextTranslateCTM(context, 0, -view.frame.origin.y);
self.layer.renderInContext(context!)
let capturedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return capturedImage
}
}
In the end you can reorder cells without having to use the handles. This can be extended for different gestures, or modes depending on the use case, but I left this as a vanilla version. Let me know if you have any questions.
Update:
Some caveats I found. It is very important you figure out how you want to use scrolling and modify this accordingly. For instance, when I mocked it out I set scrollingEnabled = false.
In the long run I did want scrolling, so I changed the code to only run this functionality on a press longer than 0.25 (I used some timers to do this). I would then temporarily disable scrolling during this period, and re-enable upon completion.
It is pretty straight forward - which is probibly why there is no explicit tutorial on the matter.
Just create the UITableView normally, but set the showsReorderControl
of each cell to TRUE. When you go into editing mode (normally by pressing the "edit" button and setting the "Editing" value of the UITableView to TRUE) - the reorder bars will appear in the cells.
Note:
If your data source implements tableView:canMoveRowAtIndexPath:
- and it returns "NO" - the reorder bars will not appear.
You will also need to implement –tableView:moveRowAtIndexPath:toIndexPath:
in the data source delegate.