如何使用多点触控一次选择多个集合视图单元格?
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: [])
}
}
}
}
我正在写一个 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: [])
}
}
}
}