CollectionView not calling didSelectItemAt correctly

I have a CollectionView in my SwiftUI App. The collectionView is wrapped in a UIViewRepresentable. If I tap on a collectionView cell normally the function isn't called but if I hold the cell down the console prints <0x107507790> Gesture: System gesture gate timed out. and when I let go of the cell after that's printed then didSelectItem is called.

Here is my CollectionView Class:

class SessionsCollectionView: UIView, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

    
    lazy var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.minimumInteritemSpacing = 0
        layout.minimumLineSpacing = 30
        
        let collectionView = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.dataSource = self
        collectionView.delegate = self
        collectionView.clipsToBounds = false
        collectionView.delaysContentTouches = false

        collectionView.register(SessionCell.self, forCellWithReuseIdentifier: "cell")
        
        collectionView.register(SessionsHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "header")

        
        return collectionView
    }()

    
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = .clear
        
        setupCollectionView()

        
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupCollectionView() {
        addSubview(collectionView)
        
        collectionView.backgroundColor = .clear
        collectionView.alwaysBounceVertical = true
        collectionView.alwaysBounceHorizontal = false
        collectionView.delaysContentTouches = false
        collectionView.contentInset = .init(top: 20, left: 0, bottom: 20, right: 0)
        collectionView.showsVerticalScrollIndicator = false
        
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: topAnchor),
            collectionView.leadingAnchor.constraint(equalTo: leadingAnchor),
            collectionView.trailingAnchor.constraint(equalTo: trailingAnchor),
            collectionView.bottomAnchor.constraint(equalTo: bottomAnchor)
        ])
    }
    
    
    
    // MARK: Header
    
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        if kind == UICollectionView.elementKindSectionHeader {
             let sectionHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath) as! Header
             
             return sectionHeader
        }
    }
    
    
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        
        let width: CGFloat = collectionView.frame.width
        let height: CGFloat = 33 + 20
        
        return CGSize(width: width, height: height)
    }
    
    
    
    // MARK: - UICollectionViewDataSource
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 10
    }
    
    
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! SessionCell
        return cell
    }
    
    
    
    // MARK: - UICollectionViewDelegateFlowLayout
    
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let width = collectionView.bounds.width
        let height = 150
        
        return CGSize(width: collectionView.bounds.width, height: height)
    }
    
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print(indexPath.row)
    }
    
    
    
}



struct SessionsCollectionViewWrapper: UIViewRepresentable {
    
    var sessionViewNavigation: SessionViewNavigation
    
    func makeUIView(context: Context) -> SessionsCollectionView {
        let sessionsCollectionView = SessionsCollectionView()
        
        return sessionsCollectionView
    }
    
    func updateUIView(_ uiView: SessionsCollectionView, context: Context) {
        // Update any properties or handle updates if needed
    }
    
}

Replies

The collection view cell is recognising taps with isHighlighted so I can get around this problem by using a delegate. Passing the indexPath to the cell then back to the collection view with the delegate method. While this technically works it's called too often and "taps" are recognised even when trying to scroll.

  • Reimplement in SwiftUI using grids.

Add a Comment

I was using this to hide the keyboard when tapped on screen (in the SwiftUI view), looks like the gesture recogniser for this was causing the issue. Once removed the collectionView works correctly.

func hideKeyboardOnTap() -> some View {
        return self.onTapGesture {
            UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
        }
    }