collectionView didn't call didSelectItemAtIndexPath when superview has gesture

∥☆過路亽.° 提交于 2019-12-08 04:15:56

问题


collectionView didn't call didSelectItemAtIndexPath when superview has tapGesture。is why?
why it print "doGesture" according to the Responder Chain?

  1. initCollectionView then add to self.view
  2. addTapGesture in self.view
  3. click item in iPhone.
  4. not call 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;
    }
    

回答1:


You need to set tapGesture.cancelsTouchesInView = NO.

Depending on Your logic You may want to check out delaysTouchesBegan too.

From Apple docs:

When the value of this property is false (the default), views analyze touch events in began and moved in parallel with the receiver. When the value of the property is true, the window suspends delivery of touch objects in the UITouchPhaseBegan phase to the view. If the gesture recognizer subsequently recognizes its gesture, these touch objects are discarded. If the gesture recognizer, however, does not recognize its gesture, the window delivers these objects to the view in a touchesBegan(:with:) message (and possibly a follow-up touchesMoved(:with:) message to inform it of the touches’ current locations). Set this property to true to prevent views from processing any touches in the UITouchPhaseBegan phase that may be recognized as part of this gesture.

EDIT : For completeness I am adding code snippet for filtering the gesture recognizer's handling, when the user taps in on the collection view. My approach is different from the one mentioned in @DonMag's answer.

- (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");        
    }
}

EDIT 2: Maybe the clearest explanation about gesture recognizers and how they work, when added in views:

Every gesture recognizer is associated with one view. By contrast, a view can have multiple gesture recognizers, because a single view might respond to many different gestures. For a gesture recognizer to recognize touches that occur in a particular view, you must attach the gesture recognizer to that view. When a user touches that view, the gesture recognizer receives a message that a touch occurred before the view object does. As a result, the gesture recognizer can respond to touches on behalf of the view.




回答2:


if "doGesture" appears in the log, then it is working as expected since you added the tap recognizer on top of your entire view. if you want the tap to be recognized AND didSelect to be called, you need to call didSelect yourself from your tap code.

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:


Set tapGesture.cancelsTouchesInView = NO

This allows touches inside other views to go through, such as collectionView didSelectItemAtIndexPath

Note that you will also get the tapGesture event. If you want to ignore that when a collectionViewCell is tapped, add this delegate method:

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


来源:https://stackoverflow.com/questions/42372609/collectionview-didnt-call-didselectitematindexpath-when-superview-has-gesture

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!