自定义 UITableViewCell 上的 UIGestureRecognizer。轻敲多个细胞

UIGestureRecognizer on custom UITableViewCell. Tapping fires on multiple cells

我在这个奇怪的问题上到处搜索都无济于事。我有一个自定义的子类 UITableViewCell。正面和背面的两个视图,点击时单元格翻转。这是这样做的:

    - (IBAction)cellFrontTapped:(id)sender {
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    if (_cellFlag == 0)
    {
        _cellFlag = 1;
        [UIView transitionFromView:_cellFrontView toView:_cellBackView
                          duration:0.5
                           options:UIViewAnimationOptionTransitionFlipFromTop | UIViewAnimationOptionShowHideTransitionViews
                        completion:NULL];
        [self performSelector:@selector(cellBackTapped:) withObject:self afterDelay:15.0];
    }
}
- (IBAction)cellBackTapped:(id)sender {
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    if (_cellFlag == 1)
    {
        _cellFlag = 0;
        [UIView transitionFromView:_cellBackView toView:_cellFrontView
                          duration:0.5
                           options:UIViewAnimationOptionTransitionFlipFromBottom | UIViewAnimationOptionShowHideTransitionViews
                        completion:NULL];
    }
}

这只适用于几个单元格。最多几页价值。但是当我加载一个更大的数据集时,点击一个单元格会像预期的那样翻转它,但也会翻转其他单元格。 我意识到这是因为细胞被 dequeueReusableCellWithIdentifier 重复使用,但我无法破译如何防止它。

我试过将它实现为一个 UIGestureRecognizer。我试过像这样使用 didSelectRowAtIndexPath

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyTableViewCell *cell = (MyTableViewCell*)[tableView cellForRowAtIndexPath:indexPath];

    if (cell.cellFlag == 0)
    {
        [cell cellFrontTapped];
    }
    else
    {
        [cell cellBackTapped];
    }
}

这些都可以正确翻转单元格,但点击事件总是翻转列表中的其他单元格。我找到的唯一解决方案是不要像这样使用 dequeueReusableCellWithIdentifier

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyTableViewCell"];
    if (cell == nil) {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"MyTableViewCell" owner:self options:nil];
        cell = [nib objectAtIndex:0];
    }

    [self configureCell:cell atIndexPath:indexPath];

    return cell;
}

这是我当前的子类实现:

@implementation MyTableViewCell

- (void)awakeFromNib {
    // Adding the recognizer here produced the same result
    //UIGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(cellTapped)];
    //tap.delegate = self;
    //[self addGestureRecognizer:tap];
    _cellFlag = 0;
}

- (void)cellFrontTapped {
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    _cellFlag = 1;
    [UIView transitionFromView:_cellFrontView toView:_cellBackView
                          duration:0.5
                           options:UIViewAnimationOptionTransitionFlipFromTop | UIViewAnimationOptionShowHideTransitionViews
                        completion:NULL];
    [self performSelector:@selector(cellBackTapped) withObject:self afterDelay:15.0];
}

- (void)cellBackTapped {
    [NSObject cancelPreviousPerformRequestsWithTarget:self];
    _cellFlag = 0;
    [UIView transitionFromView:_cellBackView toView:_cellFrontView
                          duration:0.5
                           options:UIViewAnimationOptionTransitionFlipFromBottom | UIViewAnimationOptionShowHideTransitionViews
                        completion:NULL];
}

-(void)cellTapped
{
    if (_cellFlag == 0)
    {
        [self cellFrontTapped];
    }
    else
    {
        [self cellBackTapped];
    }
}

@end

这是`configureCell:atIndexpath:

- (void)configureCell:(MyTableViewCell*)cell atIndexPath:(NSIndexPath*)indexPath
{
    NSManagedObject *t = [[self fetchedResultsController] objectAtIndexPath:indexPath];
    //here i set the labels and such
    cell.cellBackAmountLabel.text = [t.amount formattedAmount];
    //added 
    if ([self.flippedCellIndexes containsObject:indexPath])
    {
        [cell.contentView bringSubviewToFront:cell.cellBackView];
    }
    else
    {
        [cell.contentView bringSubviewToFront:cell.cellFrontView];
    }
}

我很茫然。在我的案例中,不使用 dequeueReusableCellWithIdentifier 会对性能产生重大影响。求助!

我建议将模型与视图分开。这意味着您应该跟踪哪些单元格应该呈现为翻转,而不是在单元格本身上。假设您在视图控制器中声明了一个名为 flippedIndexesNSArray(可能 NSMutableArray,甚至)。

如果你的数据集像你说的那么大,那么你可能应该使用稀疏数组而不是 NSArray。稀疏数组可以很容易地用 NSMutableDictionary 来实现。 NSMutableSet 也可以。

当单元格出列时,您将检查应该翻转哪些单元格,可能是您的 configureCell:atIndexPath: 方法。基本上,如果 indexPath 显示在您的稀疏数组上或设置您将单元格渲染为翻转。这意味着删除您在单元格上声明的 cellFlag 属性 并根据我一直提到的模型切换其状态。要翻转单元格,请检查给定 indexPathflippedIndexes 属性 并相应地执行操作。像这样:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyTableViewCell *cell = (MyTableViewCell*)[tableView cellForRowAtIndexPath:indexPath];

    if (![self.flippedIndexes containsObject:indexPath])
    {
        [cell cellFrontTapped];
        [self.flippedIndexes addObject:indexPath];
    }
    else
    {
        [cell cellBackTapped];
        [self.flippedIndexes removeObject:indexPath];
    }
}

这里我假设 NSMutableSet 用于 flippedIndexes 属性。请注意,只有在 configureCell:atIndexPath: 中检查模型时,这才会正常工作,否则滚动时单元格会神奇地清除。

这个故事的寓意是:不要在排队的单元格中存储状态信息。