UICollectionView 的 UICollectionReusableView 防止/阻止对 UICollectionViewCell 的点击

Posted

技术标签:

【中文标题】UICollectionView 的 UICollectionReusableView 防止/阻止对 UICollectionViewCell 的点击【英文标题】:UICollectionView's UICollectionReusableView preventing / blocking taps on UICollectionViewCell 【发布时间】:2017-05-16 16:12:20 【问题描述】:

我正在使用带有流布局的 UICollectionView。我已经设置了标题,这是一个 UICollectionReusableView 的行为;

layout?.sectionHeadersPinToVisibleBounds = true

...

override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView 
    let header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "header", for: indexPath as IndexPath)

    header.layer.zPosition = -1
    return header

这会产生预期的效果,即当向上滚动单元格时,标题保持固定但位于常规单元格的后面。

但是,如果我尝试单击向顶部滚动的 UICollectionViewCell,即它在技术上覆盖了 UICollectionReusableView,则 UICollectionViewCell 的 didSelectItemAt 轻击事件将不再触发,直到我将其从标题所在的位置向下滚动。换句话说,UICollectionReusableView 会阻止点击手势,即使它的 zPosition 设置为 -1 并且不可见。

有没有人遇到过这个问题,你是如何解决的?

【问题讨论】:

我没有尝试过,但听起来header.layer.zPosition = -1 只是将layer 推回去,而不是标题 view 本身(相当惊讶它完全有效,真的)。 标题“视图”仍然存在,因为您已将其固定。正如 DonMag 提到的,从视觉上看,您只是将其图层移回。似乎您根本不​​想要 header 而是其他类型的对象,您可以在布局中创建它。 我发现一个解决方法是设置header.isUserInteractionEnabled = false - 但是如果标题包含一个 UIButton(我的)那么这将破坏按钮。我认为我的解决方案是将它们全部丢弃(停止固定标题)或在滚动时切换header.isUserInteractionEnabled = false 【参考方案1】:

将此添加到您的部分标题视图类:

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool 
    return false

会将所有触摸传递给下面的下一个接收器 - 在这种情况下,您的集合视图单元格。如果您需要 Section Header 中的交互元素(例如按钮),您可以这样做:

override func point(inside point: CGPoint, with event: UIEvent?) -> Bool 
    let ptInSub = theButton.convert(point, from: theButton.superview)
    if theButton.bounds.contains(ptInSub) 
        return true
    
    return false

这个可以给你你想要的,虽然......如果单元格视图覆盖了节标题上的按钮,并且你点击按钮所在的单元格,按钮将占据轻敲。应该可以通过另一个 contains(point) 或两个来解决这个问题......

【讨论】:

【参考方案2】:

我最终在 UICollectionReusableView 标头中添加了视差效果。那里有一些 Parallax 库,但实际上非常简单 - 我使用以下代码实现了它;

override func scrollViewDidScroll(_ scrollView: UIScrollView) 

    if header != nil 

        let scrollDiff = scrollView.contentOffset.y - self.previousScrollOffset

        let absoluteTop: CGFloat = 0;
        let absoluteBottom: CGFloat = scrollView.contentSize.height - scrollView.frame.size.height;

        let isScrollingDown = scrollDiff > 0 && scrollView.contentOffset.y > absoluteTop
        let isScrollingUp = scrollDiff < 0 && scrollView.contentOffset.y < absoluteBottom

        var newHeight = self.headerHeight
        if isScrollingDown 
            newHeight = max(self.minHeaderHeight, self.headerHeight - abs(scrollDiff))
         else if isScrollingUp 
            newHeight = min(self.maxHeaderHeight, self.headerHeight + abs(scrollDiff))
        

        if newHeight != self.headerHeight 
            self.headerHeight = newHeight

            self.collectionView?.contentOffset = CGPoint(x: (self.collectionView?.contentOffset.x)!, y: self.previousScrollOffset)
            self.collectionView?.collectionViewLayout.invalidateLayout()
        

        self.previousScrollOffset = scrollView.contentOffset.y
    


然后改变页眉的高度(在你 invalidateLayout() 时调用);

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize 

    return CGSize(width: collectionView.bounds.width, height:self.headerHeight)

结果是没有发生重叠,但它仍然给出了我最初想要实现的预期效果。

【讨论】:

以上是关于UICollectionView 的 UICollectionReusableView 防止/阻止对 UICollectionViewCell 的点击的主要内容,如果未能解决你的问题,请参考以下文章

Swift 中的 UiSearchController 到 UiCollectionView

UICollectionView 具有自动调整单元格(estimatedSize)和 sectionHeadersPinToVisibleBounds 的精神

iOS开发 纯代码创建UICollectionView

根据文本调整单元格宽度 - UICollectionView - iOS

ios . -- UICollectionView --cell 自适应

自定义 UITableViewCell 的动态高度问题