为啥 UICollectionView 在滚动其单元格的滚动视图后不再调用 didSelectItemAtIndexPath ?

Posted

技术标签:

【中文标题】为啥 UICollectionView 在滚动其单元格的滚动视图后不再调用 didSelectItemAtIndexPath ?【英文标题】:why UICollectionView not calling didSelectItemAtIndexPath any more after scrolling the scrollView of it's cells?为什么 UICollectionView 在滚动其单元格的滚动视图后不再调用 didSelectItemAtIndexPath ? 【发布时间】:2014-06-19 07:44:14 【问题描述】:

我正在使用UICollectionView 来展示一些产品。在自定义UICollectionViewCell中,有一个自定义UIScrollView,里面有一些图片可以让用户快速预览。

要将轻按手势从UIScrollView 转发到UICollectionViewCell,我将覆盖与触摸相关的方法,如下所示:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

    if (!self.dragging) 
        NSLog(@"Began ==>");
        [self.nextResponder touchesBegan:touches withEvent:event];
    
    else 
        [super touchesBegan:touches withEvent:event];
    


- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

    if (!self.dragging) 
        NSLog(@"Moved ==>");
        [self.nextResponder touchesMoved:touches withEvent:event];
    
    else 
        [super touchesMoved:touches withEvent:event];
    



- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

    if (!self.dragging) 
        NSLog(@"Ended ==>");
        [self.nextResponder touchesEnded:touches withEvent:event];
    
    else 
        [super touchesEnded:touches withEvent:event];
    


- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

    if (!self.dragging) 
        NSLog(@"Cancelled ==>");
        [self.nextResponder touchesCancelled:touches withEvent:event];
    
    else 
        [super touchesCancelled:touches withEvent:event];
    

有时它在某些情况下不起作用。例如,第一次加载UICollectionView时,点击时可以调用didSelectItemAtIndexPath方法,但滚动或点击后,不再调用该方法,即使您尝试重新加载UICollectionView,也是还是行不通。

我尝试在UICollectionViewCell 的触摸方法中记录消息,触摸和事件被转发。但是,如果单元格获得手势,为什么会调用UICollectionViewdidSelectItemAtIndexPath 呢?

任何建议将不胜感激。

更新: 1.UICollectionViewCell是从nib文件中加载的。

【问题讨论】:

【参考方案1】:

如果您要定位特定对象,则使用 nextResponder 不是一个好主意。正如您所看到的,响应者链可能会发生变化,您可能并不总是得到您期望的对象。为什么会发生这种情况可能有多种原因,但无论如何您都应该在这种情况下使用更可靠的方法。

由于UIScrollViewUICollectionViewCell 的子视图,您可以直接将事件传递给您的超级视图[self.superview touchesMoved:touched withEvent:event]; 您在执行此操作时需要牢记视图层次结构,并且可能需要调用超级视图的超级视图到达细胞。

另外,我不建议使用触摸来拦截用户交互。我强烈推荐使用 UIGestureRecognizers,因为这将提供更好更一致的功能。

【讨论】:

我尝试使用self.superview。它也不可靠。实际上,手势被转发到单元格。但是没有调用 didselect 方法。很奇怪。【参考方案2】:

你应该总是调用超级方法。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 
    [super touchesBegan:touches withEvent:event];
    NSLog(@"Began ==>");


- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event 
    [super touchesMoved:touches withEvent:event];
    NSLog(@"Moved ==>");


- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event 
    [super touchesEnded:touches withEvent:event];
    NSLog(@"Ended ==>");


- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event 
    [super touchesCancelled:touches withEvent:event];
    NSLog(@"Cancelled ==>");

【讨论】:

谢谢。我的意思是,我知道,但你让我检查,我错过了 touchesCancelled 超级调用,它在滚动一点后随机地杀死了我的触摸处理。据推测,触摸处于半处理状态。 Not true “如果您在不调用 super 的情况下重写此方法(一种常见的使用模式),您还必须重写其他处理触摸事件的方法,如果只是作为存根(空)实现。” developer.apple.com/documentation/uikit/uiresponder/… 不确定:“如果您在不调用 super 的情况下覆盖此方法(一种常见的使用模式),那么您还必须覆盖处理触摸事件的其他方法,如果只是作为存根(空)实现。” developer.apple.com/documentation/uikit/uiresponder/… 不确定,developer.apple.com/documentation/uikit/uiresponder/… 不正确,如果您不调用其中的超级之一,则必须覆盖其余部分并执行相同操作(来自苹果文档)。【参考方案3】:

其实是因为self.dragging的理解。 转发点击手势的正确方法如下:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

    [self.nextResponder touchesBegan:touches withEvent:event];


- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event

    [self.nextResponder touchesMoved:touches withEvent:event];


- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

    if (!self.dragging) 
        [self.nextResponder touchesEnded:touches withEvent:event];
    
    else 
        [super touchesEnded:touches withEvent:event];
    


- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

    [self.nextResponder touchesCancelled:touches withEvent:event];

【讨论】:

以上是关于为啥 UICollectionView 在滚动其单元格的滚动视图后不再调用 didSelectItemAtIndexPath ?的主要内容,如果未能解决你的问题,请参考以下文章

为啥第一次滚动到另一个 UITableView 中的 UICollectionView 中的 UITableView 不加载数据?

为啥我的 uicollectionview 不随底层 uiscrollview 一起移动?

为啥在foreach循环中不能修改值类型实例

为啥我无法在 collectionView 中选择单元格?

如何更改 UICollectionView 中的滚动方向?

在 UICollectionView 中禁用水平滚动