Custom UITableView reorders

I am wondering if anyone has a link to a good tutorial or can point me in the right direction to recreate drag and drop cells for reordering in a UITableView, like in Epic Win App. The basic idea is that you click and hold on a list item and then drag it to where you want that item to be. Any help would be greatly appreciated.

+6
iphone cocoa-touch uikit uitableview
source share
3 answers

This is pretty straightforward - this is probably why there is no explicit tutorial on this.

Just create a UITableView as a rule, but set showsReorderControl for each cell to TRUE. When you switch to edit mode (usually by pressing the "Change" button and setting the "Edit" UITableView to TRUE) - reordering bars will appear in the cells.

Note:

If your data source implements tableView:canMoveRowAtIndexPath: - and it returns "NO" - reordering lines are not displayed.

You also need to implement –tableView:moveRowAtIndexPath:toIndexPath: in the deletion of the data source.

+7
source share

The easiest way using built-in methods is as follows:

First, set the table cell so that it displays control over the order. The simplest example (using ARC):

It is assumed that NSMutableArray *things was 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 two UITableViewDelegate methods, for example:

This method tells tableView that it can be reordered

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

Here the tableView reorders

 -(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 somewhere, set the edit to true. This is one example of how you can do this. TableView must be in edit mode so that it can be reordered.

 - (IBAction)doSomething:(id)sender { self.table.editing = YES; } 

Hope this will be a concrete example.

+13
source share

This solution will function in the same way as the OSX reminder application, which allows you to reorder cells by holding and dragging anywhere.

I created a subclass of UITableView that allows you to drag and reorder cells without having to have editing or use standard reordering descriptors.

The basic idea is that I use the started strokes and contacts to get which cell was pressed. Then I create a fake view, which is actually a screenshot of the view you are viewing, and moves it back and forth, replacing the cells in the background. When the user allows the transition, I will then show 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 rearrange the cells without using the knobs. It can be expanded for different gestures or modes depending on the use case, but I left it as a vanilla version. Let me know if you have any questions.

Update:

Some reservations that I found. It is very important to find out how you want to use scrolling and change it accordingly. For example, when I mocked this, I set scrollingEnabled = false. . In the end, I wanted to scroll, so I changed the code to use this function only when pressed for longer than 0.25 (I used some timers for this). Then I temporarily turned off scrolling during this period and turned it back on after completion.

+3
source share

All Articles