如何使用多点触控一次选择多个集合视图单元格?

How can I enable selecting multiple collection view cells at once with multitouch?

我正在写一个 iOS 纸牌游戏。我在集合视图中显示玩家的卡片。玩家可以通过点击 select 一张或多张牌,然后按下发牌按钮来发 select 张牌。

我想允许用户使用多根手指一次 select 多张卡片。例如,如果用户想要select 2张卡片,他只需要用两个手指同时点击两张卡片,它们都会select。似乎默认情况下,UICollectionView 不允许这样做。当我用 2 根手指点击时,只有一张卡片会被 selected,即使 UIView 中的 isMultipleTouchEnabled 属性 已经设置为 true。

请注意,我不是在询问如何允许用户 select 集合视图中的多个项目。我可以并且已经用 allowsMultipleSelection = true 做到了。我要问的是如何允许用户使用 2 个手指 select 2 个单元格(或使用 n 个手指的 n 个单元格)。

我找到了这个 question,但这似乎是关于如何在 selected 时显示单元格周围的边框。

我也查看了 UICollectionView 的文档,但我发现没有 属性 控制它。

首先让我们准确了解问题所在。 collectionView 附有一堆 UIGestureRecognisers(用于平移、触摸、缩放等)。每个识别器都有相同的状态机 possible->recognised->changed->ended/failed。每个识别器都有一个明确的开始和结束。一旦点击手势在一个位置开始,它就不会在另一个位置开始。当一个人 1) touched down point A 2) touched down point B 3) touched point A 4) touch up point B 手势完全忽略了 B 点,因为它在 A 点上 "focused"

第二个问题是,如果您同时触摸两个点,tapGesture.location(in: view) 的方法将给出这两个位置的平均值。

但是我们解决这个问题的第一步是禁用 collectionView tapGesture - 它没有做我们想要的事情:

  self.collectionView.allowsMultipleSelection = true
  self.collectionView.allowsSelection = false;

接下来,我们将分别向每个单元格添加我们自己的点击手势。 Apple 明确不推荐此方法 ("You should always attach your gesture recognizers to the collection view itself—not to a specific cell or view."1),但它会起作用:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
 ...
  cell.addGestureRecognizer(UITapGestureRecognizer.init(target: self, action: #selector(didTap(tapper:))))
  ...
  return cell;
}

@objc func didTap(tapper:UIGestureRecognizer) {
  if let cell = tapper.view as? UICollectionViewCell{
    if let index = collectionView.indexPath(for: cell) {
      if collectionView.indexPathsForSelectedItems?.contains(index) ?? false {
        collectionView.deselectItem(at: index, animated: true)
        cell.isSelected = false
      }else{
        collectionView.selectItem(at: index, animated: true, scrollPosition: [])
        cell.isSelected = true
      }
    }
  }
}

您可以为您想要支持的触摸次数添加多个手势识别器:

collectionView.allowsMultipleSelection = true
// allowing up to 5 finger touches, increase if you want for more :)
for i in 2...5 {
    let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
    gestureRecognizer.numberOfTouchesRequired = i
    gestureRecognizer.delegate = self
    collectionView.addGestureRecognizer(gestureRecognizer)
}

,并让控制器查找被触摸的单元格:

@objc private func handleTap(_ gestureRecognizer: UIGestureRecognizer) {
    // perform the action only after the touch ended
    guard gestureRecognizer.state == .ended else { return }

    for i in 0..<gestureRecognizer.numberOfTouches {
        let location = gestureRecognizer.location(ofTouch: i, in: collectionView)
        // if we have a cell at that point, toggle the selection
        if let indexPath = collectionView.indexPathForItem(at: location) {
            if collectionView.indexPathsForSelectedItems?.contains(indexPath) == true {
                collectionView.deselectItem(at: indexPath, animated: true)
            } else {
                collectionView.selectItem(at: indexPath, animated: true, scrollPosition: [])
            }
        }
    }
}