在移动UITableView单元格之间的部分之间添加长按手势

3

继我之前的问题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和这段代码都在重复相同的代码。
但稍后我可以清理它。
谢谢。
1个回答

2
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]) {

            [self.stackTableView beginUpdates];
            // ... update data source.
            NSLog(@"exchanging");
            [self.stackObjectArray exchangeObjectAtIndex:indexPath.section withObjectAtIndex:sourceIndexPath.section];

            // ... move the rows.
            NSLog(@"moving the rows");
            [self.stackTableView moveRowAtIndexPath:indexPath toIndexPath:sourceIndexPath];
            [self.stackTableView moveRowAtIndexPath:sourceIndexPath toIndexPath:indexPath];

            NSLog(@"ending updates");
            [self.stackTableView endUpdates];

            // ... and update source so it is in sync with UI changes.
            sourceIndexPath = indexPath;
        }
        break;
    }

好的,就是这样!

您需要做的是防止表格视图立即更新,使用beginUpdate和endUpdates同时进行所有更改。这样您就不会出现那样的错误。您还需要在同一时间内交换行,而不仅仅是移动它们。

我知道在Ray Wenderlich的示例中他只提到了移动行,所以请参考以下链接!

http://www.raywenderlich.com/63089/cookbook-moving-table-view-cells-with-a-long-press-gesture


以上代码假设我正在交换项目,但事实并非如此;我想使用长按移动行。我接受了这个答案;但我要采用的答案已经发布在我的原始问题中。 - zardon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接