问题
I want to allow the default row movement in a UITableView
without it being in editing mode, and without compromising the default behaviour of the UITableView
.

The image above displays a cell in editing mode, with movement enabled.
I tried simply running for (UIView *subview in cell.subviews)
(while my UITableView
was in editing mode), but the button didn't turn up:
<UITableViewCellScrollView: 0x8cabd80; frame = (0 0; 320 44); autoresize = W+H; gestureRecognizers = <NSArray: 0x8c9ba20>; layer = <CALayer: 0x8ca14b0>; contentOffset: {0, 0}>
How can allow/add the movement "button" without enabling editing mode in my UITableView
?
Creating and adding a UIButton
with the default function
for movement is also an option.
回答1:
I actually do something similar for one of my apps. It uses the delegate methods for table editing and a bit of 'tricking' the user. 100% built-in Apple functionality.
1 - Set the table to editing (I do it in viewWillAppear)
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.tableView setEditing:YES];
}
2 - Hide the default accessory icon:
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView
editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
//remove any editing accessories (AKA) delete button
return UITableViewCellAccessoryNone;
}
3 - Keep editing mode from moving everything to the right (in the cell)
-(BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath{
return NO;
}
4 - At this point you should be able to drag the cells around without it looking like it is in editing mode. Here we trick the user. Create your own "move" icon (three lines in the default case, whatever icon you want in your case) and add the imageView right where it would normally go on the cell.
5 - Finally, implement the delegate method to actually rearrange your underlying datasource.
-(void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath{
//Get original id
NSMutableArray *originalArray = [self.model.items objectAtIndex:sourceIndexPath.section];
Item * original = [originalArray objectAtIndex:sourceIndexPath.row];
//Get destination id
NSMutableArray *destinationArray = [self.model.items objectAtIndex:destinationIndexPath.section];
Item * destination = [destinationArray objectAtIndex:destinationIndexPath.row];
CGPoint temp = CGPointMake([original.section intValue], [original.row intValue]);
original.row = destination.row;
original.section = destination.section;
destination.section = @(temp.x);
destination.row = @(temp.y);
//Put destination value in original array
[originalArray replaceObjectAtIndex:sourceIndexPath.row withObject:destination];
//put original value in destination array
[destinationArray replaceObjectAtIndex:destinationIndexPath.row withObject:original];
//reload tableview smoothly to reflect changes
dispatch_async(dispatch_get_main_queue(), ^{
[UIView transitionWithView:tableView duration:duration options:UIViewAnimationOptionTransitionCrossDissolve animations:^{
[tableView reloadData];
} completion:NULL];
});
}
回答2:
William Falcon's answer in swift 3
1 - Set the table to editing (I do it in viewWillAppear)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated: animated)
tableView.setEditing(true, animated: false)
}
2 - Hide the default accessory icon:
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return .none
}
3 - Keep editing mode from moving everything to the right (in the cell)
override func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
return false
}
4 - Not required in swift 3
5 - Reorder your array
Extra Note
If you want to have your table cells selectable, add the following code within viewWillAppear()
function.
tableView.allowsSelectionDuringEditing = true
回答3:
If you don't want to set the UITableView
into editing mode then you will need to reinvent the capability for dragging cells around.
I present a fairly complete solution that enables the user to move rows around within the visible area of the UITableView
. It works whether the UITableView
is in editing mode or not. You'll need to extend it if you want the table to scroll when a row is dragged near the top or bottom of the visible area. There are likely some failure and edge cases you'll need to ferret out as well.
@implementation TSTableViewController
{
NSMutableArray* _dataSource;
}
- (void) viewDidLoad
{
[super viewDidLoad];
_dataSource = [NSMutableArray new];
for ( int i = 0 ; i < 10 ; i++ )
{
[_dataSource addObject: [NSString stringWithFormat: @"cell %d", i]];
}
}
- (void) longPress: (UILongPressGestureRecognizer*) lpgr
{
static NSString* dragCellData = nil;
static UIView* dragCellView = nil;
static NSInteger dragCellOffset = 0;
// determine the cell we're hovering over, etc:
CGPoint pt = [lpgr locationInView: self.tableView];
NSIndexPath* ip = [self.tableView indexPathForRowAtPoint: pt];
UITableViewCell* cell = [self.tableView cellForRowAtIndexPath: ip];
CGPoint ptInCell = [lpgr locationInView: cell];
// where the current placeholder cell is, if any:
NSInteger placeholderIndex = [_dataSource indexOfObject: @"placeholder"];
switch ( lpgr.state )
{
case UIGestureRecognizerStateBegan:
{
// get a snapshot-view of the cell we're going to drag:
cell.selected = cell.highlighted = NO;
dragCellView = [cell snapshotViewAfterScreenUpdates: YES];
dragCellView.clipsToBounds = NO;
dragCellView.layer.shadowRadius = 10;
dragCellView.layer.shadowColor = [UIColor blackColor].CGColor;
dragCellView.layer.masksToBounds = NO;
dragCellView.frame = [cell convertRect: cell.bounds
toView: self.tableView.window];
// used to position the dragCellView nicely:
dragCellOffset = ptInCell.y;
// the cell will be removed from the view hierarchy by the tableview, so transfer the gesture recognizer to our drag view, and add it into the view hierarchy:
[dragCellView addGestureRecognizer: lpgr];
[self.tableView.window addSubview: dragCellView];
// swap out the cell for a placeholder:
dragCellData = _dataSource[ip.row];
_dataSource[ip.row] = @"placeholder";
[self.tableView reloadRowsAtIndexPaths: @[ip]
withRowAnimation: UITableViewRowAnimationNone];
break;
}
case UIGestureRecognizerStateChanged:
{
// where should we move the placeholder to?
NSInteger insertIndex = ptInCell.y < cell.bounds.size.height / 2.0 ? ip.row : ip.row + 1;
if ( insertIndex != placeholderIndex )
{
// remove from the datasource and the tableview:
[_dataSource removeObjectAtIndex: placeholderIndex];
[self.tableView deleteRowsAtIndexPaths: @[ [NSIndexPath indexPathForRow: placeholderIndex inSection: 0] ]
withRowAnimation: UITableViewRowAnimationFade];
// adjust:
if ( placeholderIndex < insertIndex )
{
insertIndex--;
}
// insert to the datasource and tableview:
[_dataSource insertObject: @"placeholder"
atIndex: insertIndex];
[self.tableView insertRowsAtIndexPaths: @[ [NSIndexPath indexPathForRow: insertIndex inSection: 0] ]
withRowAnimation: UITableViewRowAnimationFade];
}
// move our dragCellView
CGRect f = dragCellView.frame;
f.origin.y = pt.y - dragCellOffset;
dragCellView.frame = f;
break;
}
case UIGestureRecognizerStateEnded:
{
// replace the placeholdercell with the cell we were dragging
[_dataSource replaceObjectAtIndex: placeholderIndex
withObject: dragCellData];
[self.tableView reloadRowsAtIndexPaths: @[ [NSIndexPath indexPathForRow: placeholderIndex inSection: 0] ]
withRowAnimation: UITableViewRowAnimationFade];
// reset state
[dragCellView removeFromSuperview];
dragCellView = nil;
dragCellData = nil;
break;
}
default:
{
break;
}
}
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _dataSource.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString* cellData = _dataSource[indexPath.row];
if ( [cellData isEqualToString: @"placeholder" ] )
{
// an empty cell to denote where the "drop" would go
return [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
reuseIdentifier: nil];
}
// a cell...
UITableViewCell* cell = [[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
reuseIdentifier: nil];
// our "fake" move handle & gesture recognizer
UILongPressGestureRecognizer* lpgr = [[UILongPressGestureRecognizer alloc] initWithTarget: self action: @selector( longPress:) ];
lpgr.minimumPressDuration = 0.3;
UILabel* dragLabelView = [UILabel new];
dragLabelView.text = @"☰";
dragLabelView.userInteractionEnabled = YES;
[dragLabelView addGestureRecognizer: lpgr];
[dragLabelView sizeToFit];
cell.textLabel.text = cellData;
if ( tableView.isEditing )
{
cell.editingAccessoryView = dragLabelView;
}
else
{
cell.accessoryView = dragLabelView;
}
return cell;
}
@end
来源:https://stackoverflow.com/questions/22849825/ios-allow-default-row-movement-in-uitableview-without-editing-mode