当superview有手势时collectionView没有调用didSelectItemAtIndexPath

Posted

技术标签:

【中文标题】当superview有手势时collectionView没有调用didSelectItemAtIndexPath【英文标题】:collectionView didn't call didSelectItemAtIndexPath when superview has gesture 【发布时间】:2017-02-21 16:20:03 【问题描述】:

当superview有tapGesture时collectionView没有调用didSelectItemAtIndexPath。这是为什么? 为什么它会根据响应者链打印“doGesture”?

    initCollectionView 然后添加到 self.view 在 self.view 中添加TapGesture 在 iPhone 中单击项目。

    不调用 didSelectItemAtIndexPath。

    - (void)viewDidLoad 
        [super viewDidLoad];
        UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
        self.collectionView = [[MyCollectionView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height - 100) collectionViewLayout:flowLayout];
        [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"abc"];
        self.collectionView.delegate = self;
        self.collectionView.dataSource = self;
        [self.view addSubview:self.collectionView];
    
        UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(doGesture)];
        tapGesture.delegate = self;
        [self.view addGestureRecognizer:tapGesture];
    
    
    - (void)doGesture
    
        NSLog(@"%@",@"doGesture");
    
    
    - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
    
        return 100;
    
    
    - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
    
        NSLog(@"%@",@"didSelectItemAtIndexPath");
    
    
    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
    
        UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"abc" forIndexPath:indexPath];
        if (indexPath.row %2==0) 
            cell.backgroundColor = [UIColor redColor];
         else
            cell.backgroundColor = [UIColor grayColor];
        
        return cell;
    
    

【问题讨论】:

"not call didSelectItemAtIndexPath" 是否调用了doGesture? ——让我换一种说法。假设您删除了[self.view addGestureRecognizer:tapGesture] 行。 现在单元格选择起作用了吗? 为什么要在collectionView上添加手势?您的流程/功能要求是什么? 【参考方案1】:

你需要设置tapGesture.cancelsTouchesInView = NO

根据您的逻辑,您可能还想查看delaysTouchesBegan

来自 Apple 文档:

当该属性的值为false(默认)时,视图分析 触摸事件开始并与接收器平行移动。什么时候 该属性的值为真,窗口暂停交付 在 UITouchPhaseBegan 阶段触摸对象到视图。如果 手势识别器随后识别其手势,这些触摸 对象被丢弃。但是,如果手势识别器不 识别它的手势,窗口将这些对象传递给视图 在 touchesBegan(:with:) 消息中(可能还有后续 touchesMoved(:with:) 消息通知它当前的触摸 地点)。将此属性设置为 true 以防止处理视图 UITouchPhaseBegan 阶段中的任何可能被识别为的触摸 这个手势的一部分。

编辑: 为了完整起见,我添加了代码 sn-p 以在用户点击集合视图时过滤手势识别器的处理。我的方法与@DonMag 的回答中提到的方法不同。

- (void)doGesture:(UIGestureRecognizer*) sender
    
    CGPoint locationInView = [sender locationOfTouch:0 inView:self.view];
    CGPojnt convertedLocation = [self.collectionView convertPoint:location fromView:self.view];

    // from Apple doc
    // Returns a Boolean value indicating whether the receiver contains the specified point.
    if (![self.collectionView pointInside:convertedLocation withEvent:nil])
    
      NSLog(@"%@",@"doGesture");        
    

编辑 2: 当添加到视图中时,也许是关于手势识别器及其工作方式的最清晰的解释:

每个手势识别器都与一个视图相关联。相比之下,一个 视图可以有多个手势识别器,因为单个视图 可能会响应许多不同的手势。对于手势识别器 识别在特定视图中发生的触摸,您必须附加 该视图的手势识别器。当用户触摸该视图时, 手势识别器接收到在 视图对象确实如此。结果,手势识别器可以响应 touchs 代表视图。

【讨论】:

谢谢!现在可以了。结果打印“doGesture”和“didSelectItemAtIndexPath”。但是为什么根据响应链打印“doGesture”? 原因是您添加的手势识别器在集合视图的父视图中。在点击集合视图时,我添加了一个代码 sn-p,它采用不同的方法过滤手势识别器。 我就是不明白为什么不默认collectionView来处理触摸事件 我已经通过第二次编辑扩展了我的答案,这将回答您的问题。 ios 响应顺序为 UICollectionViewCell -> UICollectionView -> self.view。当 UICollectionView 的响应应该是句柄,但结果是 self.view.【参考方案2】:

如果“doGesture”出现在日志中,那么它按预期工作,因为您在整个视图顶部添加了点击识别器。如果您希望点击被识别并调用 didSelect,您需要从您的点击代码中自己调用 didSelect。

let locationInView: CGPoint = tapGesture.location(in: self.view)
let locationInCollectionView: CGPoint = tapGesture.location(in: self.collectionView)
if let indexPath: IndexPath = collectionView.indexPathForItem(at: locationInCollectionView) 
    collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .centeredVertically)

【讨论】:

【参考方案3】:

设置tapGesture.cancelsTouchesInView = NO

这允许其他视图内的触摸通过,例如collectionView didSelectItemAtIndexPath

请注意,您将获得 tapGesture 事件。如果您想在点击 collectionViewCell 时忽略它,请添加此委托方法:

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
    CGPoint touchPoint = [touch locationInView:self.collectionView];
    return ![self.collectionView hitTest:touchPoint withEvent:nil];

【讨论】:

以上是关于当superview有手势时collectionView没有调用didSelectItemAtIndexPath的主要内容,如果未能解决你的问题,请参考以下文章

如何允许某些手势通过 superview 到它下面的视图?

防止子视图在其 Superview 转换时进行转换

在使用手势识别器添加子视图时遇到 UI 故障问题

如何将手势识别器从一个视图传递到另一个视图

尝试将参数传递给点击手势的方法

当 SuperViews 约束改变时更新 SubView 约束