【问题标题】:How to remove a UIGestureRecognizer from a table cell如何从表格单元格中删除 UIGestureRecognizer
【发布时间】:2011-12-02 16:58:20
【问题描述】:

我只想允许在UITableView 的第一个单元格上滑动删除。 这一点很简单,但是当用户尝试滑动任何其他单元格时,我想显示UIAlert。我再次通过在除单元 0 之外的每个单元上使用 UIGestureRecognizer 来实现此功能。

我遇到的问题是,一旦删除了顶行,我希望允许删除新的顶行。 好像我需要删除分配给单元格的UIGestureRecognizer's,但我不知道该怎么做。

这是我的一些代码

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    BetCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BetCell"];

    Bet *bet = [self.bets objectAtIndex:([self.bets count]-indexPath.row-1)];
    cell.BFNeedLabel.text = bet.BFNeeded;

    if (indexPath.row != 0) {
    UISwipeGestureRecognizer *swipeRecognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeDetected:)];
    swipeRecognizer.direction = (UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight);
    [cell addGestureRecognizer:swipeRecognizer];
    }

    return cell;

}

-(void)swipeDetected:(UIGestureRecognizer *)sender
{
    UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Info" message:@"You can only delete starting from the top cell" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alert show];
}

-(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row == 0) {
        return YES;
    } else  { 
        return NO;
    }
}

-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    // If the very first row
    if ((editingStyle == UITableViewCellEditingStyleDelete) && (indexPath.row == 0)) {
        Bet *betObj = [self.bets objectAtIndex:([self.bets count]-indexPath.row-1)];
        //Delete from array
        [self.bets removeObjectAtIndex:([self.bets count]-indexPath.row-1)];
        //Delete the row
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];

        //Attempt to remove gesture recognizer from cell 0
        UISwipeGestureRecognizer *swipeRecognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeDetected:)];
        swipeRecognizer.direction = (UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight);
        [[tableView cellForRowAtIndexPath:0]removeGestureRecognizer:swipeRecognizer];
    }
}

【问题讨论】:

    标签: ios uitableview xcode4.2 uigesturerecognizer


    【解决方案1】:

    无需移除gestureRecognizer。在您的swipeDetected 中找出您所在的单元格,并且仅在indexPath.row != 0 时显示警报。

    您的手势识别器将为您提供可转换为表格视图坐标空间的位置,该坐标空间可用于获取该单元格的 indexPath。

    swipeDetected

    CGPoint location = [sender locationInView:self.tableView];
    NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
    if ( indexPath.row != 0 {
    // do alert
    }
    

    更新示例代码:

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {
        return 1;
    }
    
    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return 20;
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Test"];
        if ( !cell ) {
            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"test"] autorelease];
            UISwipeGestureRecognizer *gr = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(handleCellSwipe:)];
            gr.direction = UISwipeGestureRecognizerDirectionRight + UISwipeGestureRecognizerDirectionLeft;
            gr.delegate = self;
            [cell addGestureRecognizer:gr];
            [gr release];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"Cell %d",indexPath.row];
        return cell;
    }
    
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        [tableView deselectRowAtIndexPath:indexPath animated:YES];
    }
    
    - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if ( indexPath.row == 0 )
            return UITableViewCellEditingStyleDelete;
        else 
            return UITableViewCellEditingStyleNone;
    }
    
    - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return indexPath.row == 0;
    }
    
    - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
    {
        // do stuff
    }
    
    - (void)handleCellSwipe:(UIGestureRecognizer *)gestureRecognizer
    {
        if ( gestureRecognizer.state == UIGestureRecognizerStateRecognized ) {
            CGPoint location = [gestureRecognizer locationInView:self.tableView];
            NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
            if ( indexPath.row != 0 ) {
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Cell Swipe"
                                                                message:@"Cell in row not equal to 0 swiped"
                                                               delegate:nil 
                                                      cancelButtonTitle:nil
                                                      otherButtonTitles:@"OK", nil];
                [alert show];
                [alert release];
            }
        }
    }
    
    - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
    {
        CGPoint location = [touch locationInView:self.tableView];
        NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:location];
        return indexPath.row != 0;
    }
    

    【讨论】:

    • 谢谢,我快到了。这现在不会在新的顶行显示警报,但它不会显示删除按钮,因为滑动正在运行 swipeDetected 代码,而不是像在删除之前的第一个代码那样运行 commitEditingStyle 代码。
    • 嗯...好点。使单元格成为手势识别器的代表,实现- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch; 并使用与swipeDetected 类似的逻辑,如果indexPath.row==0 则返回NO
    • 编辑评论太晚了:让控制器成为代表,而不是单元格。想到另一个我也在研究的问题。
    • 它似乎仍然无法正常工作。我有控制器作为代表,并且有这个似乎没有什么区别:- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { CGPoint location = [touch locationInView:self.tableView]; NSIndexPath *indexpath = [self.tableView indexPathForRowAtPoint:location]; if (indexpath.row == 0) { return NO; } else { return YES; } }
    • 嗯...我只是整理了一个快速示例项目并在您描述您希望它工作时对其进行测试。我会发布代码。
    【解决方案2】:

    当您可以通过 caneditrowatindexpath 显示警报时,为什么还要经历手势识别器的麻烦。我能看到的唯一问题是,如果您还使用编辑按钮 - 那么您会一次收到很多警报。

    -(BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (indexPath.row == 0) {
            return YES;
        } else  { 
            UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"Info" message:@"You can only delete starting from the top cell" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
            [alert show];
        }
    }
    

    如果您设置使用手势识别器,您只需在删除后的索引 0 上使用 reloadRowsAtIndexPaths:withRowAnimation: 这将重置第一个单元格。但是,您在原始帖子中的 cellforrowatindexpath 设置错误,无论哪种方式都会导致此设置发生奇怪的事情。

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        BetCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BetCell"];
    
        if ( !cell ) //this is needed in case a cell was never created in the first place
            cell = [[[BetCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"BetCell"] autorelease];
    
        Bet *bet = [self.bets objectAtIndex:([self.bets count]-indexPath.row-1)];
        cell.BFNeedLabel.text = bet.BFNeeded;
    
        if (indexPath.row != 0) {
            UISwipeGestureRecognizer *swipeRecognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeDetected:)];
            swipeRecognizer.direction = (UISwipeGestureRecognizerDirectionLeft | UISwipeGestureRecognizerDirectionRight);
            [cell addGestureRecognizer:swipeRecognizer];
        } else {//this is needed to reset a cell that is being reused (such as when you scroll off the page and return)
            for (UIGestureRecognizer *gesture in cell.gestureRecognizers) {
                [cell removeGestureRecognizer:gesture];
            }
        }
    
        return cell;
    
    }
    

    【讨论】:

    • 我让它像上面一样工作,还有更多的代码。我不想只使用 canEditRow 的原因是我不希望编辑按钮显示在除第 0 行之外的行上,但我不希望其他行上什么都没有发生,所以用户认为删除不是一个选项.现在,如果他们尝试滑动除第 0 行以外的任何行,他们会收到警报