带有 UIScrollView 的 UICollectionViewCell 取消了 didSelectItemAtIndexPath

Posted

技术标签:

【中文标题】带有 UIScrollView 的 UICollectionViewCell 取消了 didSelectItemAtIndexPath【英文标题】:UICollectionViewCell with UIScrollView cancels didSelectItemAtIndexPath 【发布时间】:2013-01-12 22:33:31 【问题描述】:

我有一个UICollectionView,它可以水平滚动以一次显示一个UICollectionViewCell。每个UICollectionViewCell 都有一个垂直滚动的UIScrollView 作为子视图,用于滚动单元格的内容。 UICollectionViewCell 的内部只有 90 % 左右被 UIScrollView 覆盖 - 即单元格的外框未被此覆盖。

事实证明,UICollectionViewCell 中被UIScrollView 覆盖的部分取消了UICollectionView 委托didSelectItemAtIndexPath。因此,当在UIScrollView 内发生简单的点击时,此方法 被调用,而如果点击发生在单元格的外部,即UIScrollView 之外,则此方法 调用。

关于如何实现即使在UIScrollView 内点击也可以调用didSelectItemAtIndexPath 方法的设置的任何建议?

【问题讨论】:

【参考方案1】:

我发现最有效的方法是窃取UIScrollView暴露的panGestureRecognizer,并禁用scrollView上的userInteraction。这样,您可以获得滚动视图的行为,但保持集合视图上的交互。在您的 UICollectionViewCell 子类上:

self.scrollView.userInteractionEnabled = NO;
[self.contentView addGestureRecognizer:self.scrollView.panGestureRecognizer];

这是 Apple 在 WWDC 2014 session 235(高级滚动视图和触摸处理技术)中推荐和演示的方法

【讨论】:

这个解决方案似乎是迄今为止解决这个问题的最干净的方法。谢谢! 这是一个很棒的解决方案,但如果你的 scrollView 包含按钮,它就不起作用:( 是的,这是最好的解决方案,因为它只需要 2 行代码,您仍然可以使用 didSelectItemAtIndexPath 函数。干得好!【参考方案2】:

这是一个 UIScrollView 子类方法,它维护单元格选择功能,还允许在滚动视图内外进行 UIControl 选择(按钮等)。

斯威夫特 3

class CellContentScrollView: UIScrollView 

  override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? 
    let hitTargetView = super.hitTest(point, with: event)
    return hitTargetView as? UIControl ?? (hitTargetView == self ? nil : superview)
  

  override func didMoveToSuperview() 
    superview?.addGestureRecognizer(panGestureRecognizer)
  


斯威夫特 2

class CellContentScrollView: UIScrollView 

    // MARK: - UIView override

    override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? 
        let hitTargetView = super.hitTest(point, withEvent: event)
        return hitTargetView as? UIControl ?? (hitTargetView == self ? nil : superview)
    

    override func didMoveToSuperview() 
        superview?.addGestureRecognizer(panGestureRecognizer)
    

Objective-C

@implementation CellContentScrollView

    #pragma mark - UIView override

    - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 
        UIView *hitTargetView = [super hitTest:point withEvent:event];

        if ([hitTargetView isKindOfClass:UIControl.class]) 
            return hitTargetView;
         else if (hitTargetView != self) 
            return self.superview;
        

        return nil;
    

    - (void)didMoveToSuperview 
        [self.superview addGestureRecognizer:self.panGestureRecognizer];
    

@end

【讨论】:

完美运行!谢谢 你拯救了我的一天!谢谢!【参考方案3】:

点击UIScrollView 用于查看是否应该滚动。

您应该将catch the single tap on the UIScrollView 本身传递给周围的UICollectionViewCell

【讨论】:

谢谢!我发现这个答案好一点:***.com/a/5216518/746968 @Zappel 那么如何将点击转发到 UICollectionView 以模拟一个交互式选择点击,该点击也会触发 UICollectionView 的选择委托方法? -1 你应该明确地解释你将如何“将它传递给周围的 UICollectionViewCell”【参考方案4】:

为您的超级视图添加点按手势

override func viewDidLoad() 
        super.viewDidLoad()
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTapView(gesture:)))
        view.addGestureRecognizer(tapGesture)
    

@objc func didTapView(gesture: UITapGestureRecognizer) 
    view.endEditing(true)
    let touchLocation:CGPoint = gesture.location(ofTouch: 0, in: self.collectionView)
    let indexPath = self.collectionView.indexPathForItem(at: touchLocation)
    if indexPath != nil 
        let cell = self.collectionView.cellForItem(at: indexPath!)
        if (cell?.isSelected)! 
            //PREFORM DESELECT
         else 
            //PREFORM SELECT HERE
        
    
 

【讨论】:

以上是关于带有 UIScrollView 的 UICollectionViewCell 取消了 didSelectItemAtIndexPath的主要内容,如果未能解决你的问题,请参考以下文章

带有 IUViewController 的 UIScrollView 拖放对象

带有 ViewControllers 的 UIScrollView

带有 UIScrollView 的纯自动布局

带有 UIScrollView 的 ECSlidingViewController

带有 UIScrollView 的 UIPanGestureRecognizer

带有背景图像的 UIScrollView