【问题标题】:Adding longpress gesture on moving uitableviewcells between sections在部分之间移动 uitableviewcells 添加长按手势
【发布时间】:2015-05-07 10:13:05
【问题描述】:

继续我之前的问题 moveRowAtIndexPath - Moving cells between sections

我已经能够使用传统的moveRowAtIndexPath 在部分之间移动单元格,即:将标记从销售移动到营销。

但是现在我想更进一步,在表格视图上添加长按手势,并允许我移动单元格,而不是简单地在单元格上使用长按。

作为参考,我的表格是Employees 的列表,他们在不同的Departments 工作;其中模型是自定义的,但只包含简单的名称字符串。

我的行设置是这样的;

-(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 期间将 longpress 添加到表格中,ala;

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 中崩溃了,它试图移动单元格;它与移动前后数组的不一致有关。

另外,我不确定我是否做得对;这种模式是否适用于基于部分的部门和员工列表?

无论如何,我的查询是:如何将长按手势添加到由部分组成的 tableview 单元格,或者..我想使用长按将 Tom 从 Sales 移至 Marketing。

鉴于上面的代码,这可能吗?

编辑;

我得到的崩溃是:

'NSInternalInconsistencyException',原因:'无效更新:无效 第 0 节中的行数。包含在第 0 节中的行数 更新后的现有节(3)必须等于 更新之前该部分中包含的行 (3),加号或减号 从该部分插入或删除的行数(0 插入, 0 已删除)加或减移入或移出的行数 该部分(0 移入,1 移出)。 *** 首先 throw 调用 st


进一步更新:@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 和这段代码都在重复相同的代码。

但我可以稍后清理。

谢谢

【问题讨论】:

    标签: objective-c uitableview uigesturerecognizer long-press


    【解决方案1】:
    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

    【讨论】:

    • 上面的代码假设我在交换物品,但事实并非如此;我想使用 Longpress 移动行。答案被接受;但我要回答的问题已发布在我的原始问题中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-19
    • 1970-01-01
    • 1970-01-01
    • 2019-05-13
    • 1970-01-01
    • 2013-05-06
    相关资源
    最近更新 更多