继我之前的问题moveRowAtIndexPath - 移动单元格在不同的分区之间
我已经能够像传统方法一样使用moveRowAtIndexPath
移动单元格在不同的分区之间,例如:将Mark从销售部门移动到市场部门。
然而,现在我想进一步添加长按手势到tableview,并允许我仅通过长按单元格来移动单元格。
作为参考,我的表是一个列表,其中包含在不同部门工作的员工
; 模型是自定义的,但只包含简单的字符串名称。
我的行设置如下;
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return [_objects count];
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
Department *department = [_objects objectAtIndex:section];
return [department.employees count];
}
我目前的moveRowAtIndexPath代码如下:
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
if (fromIndexPath != toIndexPath ) {
Department *departmentFrom = [_objects objectAtIndex:fromIndexPath.section];
Department *departmentTo = [_objects objectAtIndex:toIndexPath.section];
Employee *employee = [departmentFrom.employees objectAtIndex:fromIndexPath.row];
[departmentFrom.employees removeObjectAtIndex:fromIndexPath.row];
[departmentTo.employees insertObject:employee atIndex:toIndexPath.row];
[tableView reloadData];
}
}
所以现在我在viewDidLoad中添加了长按表格的功能,就像这样;
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressGestureRecognized:)];
[self.tableView addGestureRecognizer:longPress];
然后最终有我的长按代码本身;
- (IBAction)longPressGestureRecognized:(id)sender {
UILongPressGestureRecognizer *longPress = (UILongPressGestureRecognizer *)sender;
UIGestureRecognizerState state = longPress.state;
CGPoint location = [longPress locationInView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
static UIView *snapshot = nil; ///< A snapshot of the row user is moving.
static NSIndexPath *sourceIndexPath = nil; ///< Initial index path, where gesture begins.
switch (state) {
case UIGestureRecognizerStateBegan: {
if (indexPath) {
sourceIndexPath = indexPath;
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
// Take a snapshot of the selected row using helper method.
snapshot = [self customSnapshoFromView:cell];
// Add the snapshot as subview, centered at cell's center...
__block CGPoint center = cell.center;
snapshot.center = center;
snapshot.alpha = 0.0;
[self.tableView addSubview:snapshot];
[UIView animateWithDuration:0.25 animations:^{
// Offset for gesture location.
center.y = location.y;
snapshot.center = center;
snapshot.transform = CGAffineTransformMakeScale(1.05, 1.05);
snapshot.alpha = 0.98;
cell.alpha = 0.0;
} completion:^(BOOL finished) {
cell.hidden = YES;
}];
}
break;
}
case UIGestureRecognizerStateChanged: {
CGPoint center = snapshot.center;
center.y = location.y;
snapshot.center = center;
// Is destination valid and is it different from source?
if (indexPath && ![indexPath isEqual:sourceIndexPath]) {
// ... update data source.
// ... move the rows.
[self.tableView moveRowAtIndexPath:sourceIndexPath toIndexPath:indexPath];
// ... and update source so it is in sync with UI changes.
sourceIndexPath = indexPath;
}
break;
}
default: {
// Clean up.
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:sourceIndexPath];
cell.hidden = NO;
cell.alpha = 0.0;
[UIView animateWithDuration:0.25 animations:^{
snapshot.center = cell.center;
snapshot.transform = CGAffineTransformIdentity;
snapshot.alpha = 0.0;
cell.alpha = 1.0;
} completion:^(BOOL finished) {
sourceIndexPath = nil;
[snapshot removeFromSuperview];
snapshot = nil;
}];
break;
}
}
}
#pragma mark - Helper methods
/** @brief Returns a customized snapshot of a given view. */
- (UIView *)customSnapshoFromView:(UIView *)inputView {
// Make an image from the input view.
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, NO, 0);
[inputView.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Create an image view.
UIView *snapshot = [[UIImageView alloc] initWithImage:image];
snapshot.layer.masksToBounds = NO;
snapshot.layer.cornerRadius = 0.0;
snapshot.layer.shadowOffset = CGSizeMake(-5.0, 0.0);
snapshot.layer.shadowRadius = 5.0;
snapshot.layer.shadowOpacity = 0.4;
return snapshot;
}
不幸的是,这个问题出现在
UIGestureRecognizerStateChanged
中,当它试图移动单元格时崩溃了;这与移动前/后数组的不一致有关。此外,我不确定我的做法是否正确;这种模式是否适用于部门和员工的分组列表?
无论如何,我的问题是:如何将长按手势添加到由部分组成的表视图单元格中,或者...我想使用长按将Tom从销售移动到营销?
鉴于上述代码,这可能吗?
编辑:
我得到的崩溃信息是:
'NSInternalInconsistencyException',原因:'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (3) must be equal to the number of rows contained in that section before the update (3), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 1 moved out).'
进一步更新:@14-Jan-2016
结果发现被接受的答案仍然导致我的应用崩溃。原因是它假定已经进行了交换;但事实并非如此。
我通过重复删除一个项目,然后重新添加回来来解决了这个问题。
下面是我的代码:
case UIGestureRecognizerStateChanged: {
CGPoint center = snapshot.center;
center.y = location.y;
center.x = location.x;
snapshot.center = center;
// Is destination valid and is it different from source?
if (indexPath && ![indexPath isEqual:sourceIndexPath])
{
Department *departmentFrom = [_objects objectAtIndex:sourceIndexPath.section];
Department *departmentTo = [_objects objectAtIndex:indexPath.section];
[self.tableView beginUpdates];
Employee *employee = [departmentFrom.employees objectAtIndex:sourceIndexPath.row];
[departmentFrom.employees removeObjectAtIndex:sourceIndexPath.row];
[departmentTo.employees insertObject:employee atIndex:indexPath.row];
[self.tableView moveRowAtIndexPath:sourceIndexPath toIndexPath:indexPath];
[self.tableView endUpdates];
// ... and update source so it is in sync with UI changes.
sourceIndexPath = indexPath;
}
break;
}
我唯一能看到的问题就是tableView:moveRowAtIndexPath和这段代码都在重复相同的代码。
但稍后我可以清理它。
谢谢。