🎣

UITapGestureRecognizer を使うと他のUIViewが反応しない!cancelsTouchesInView の挙動

2024/02/18に公開

環境

Ventura 13.6.4
Xcode 15.2
シミュレータ iOS17.2

構造

A: UIView
AG: Aに付けた UITapGestureRecognizer
B: Aに乗っている UIButton
C: Aに乗っている UICollectionView

挙動 cancelsTouchesInView = true (デフォルト)

ボタンを押したとき

  • AGは無反応
  • ボタンは反応

セルを押したとき

  • AGは反応
  • コレクションビューは無反応

挙動 cancelsTouchesInView = false

ボタンを押したとき

  • AGは反応
  • ボタンは反応

セルを押したとき

  • AGは反応
  • コレクションビューは反応

ソース

import UIKit

class ViewController: UIViewController, UICollectionViewDelegate {
    
    var dataSource: UICollectionViewDiffableDataSource<String, String>! = nil
    var collectionView: UICollectionView! = nil    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        configureHierarchy()
        configureDataSource()
        
        let b = UIButton(type: .system)
        b.setTitle("ボタン", for: .normal)
        b.addAction(UIAction(handler: { _ in
            print("ボタンが押された")
        }), for: .touchUpInside)
        b.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
        
        collectionView.frame = CGRect(x: 100, y: 200, width: 100, height: 100)

        view.addSubview(b)
        view.addSubview(collectionView)
        
        let g = UITapGestureRecognizer(target: self, action: #selector(gPushed))
        g.cancelsTouchesInView = false
        view.addGestureRecognizer(g)
    }
    
    @objc func gPushed() {
        print("ジェスチャー")
    }
    
    func configureHierarchy() {
        collectionView = UICollectionView(
            frame: view.bounds,
            collectionViewLayout: createLayout())
        view.addSubview(collectionView)
        collectionView.delegate = self //タップ操作へ対応するため
    }
    
    func createLayout() -> UICollectionViewLayout {
        let config = UICollectionLayoutListConfiguration(appearance: .grouped)
        return UICollectionViewCompositionalLayout.list(using: config)
    }
    
    func configureDataSource() {
        //【C】リサイクル機能
        let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, String> {
            (cell, indexPath, identifier) in
            var content = cell.defaultContentConfiguration()
            content.text = identifier
            cell.contentConfiguration = content
        }
        
        //データソースの作成
        dataSource = UICollectionViewDiffableDataSource<String, String> (collectionView: collectionView) {
            //【B】セルの内容が決定
            (collectionView: UICollectionView, indexPath: IndexPath, identifier: String) -> UICollectionViewCell? in
            return collectionView.dequeueConfiguredReusableCell(
                using: cellRegistration,
                for: indexPath,
                item: identifier)
        }
        
        //【A】構成を決める
        var snapshot = NSDiffableDataSourceSnapshot<String, String>()
        snapshot.appendSections(["one"])
        snapshot.appendItems(["one"], toSection: "one")
        dataSource.apply(snapshot, animatingDifferences: false)
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("Cell")
    }
}

反応しない、UIButton、UICollectionView、UITableView

Discussion