I’m experiencing an issue with UICollectionView
where the cells become unresponsive to touch events during animations, even though I’m using the .allowUserInteraction
option. I’ve created a minimal reproducible example to demonstrate the problem.
Problem Description
In my UICollectionView
, I want to resize cells when they are selected. The resizing works as expected, but the cells that are participating in the animations do not respond to scroll or touch events during the animation. I’ve tried using the .allowUserInteraction
option, but it doesn’t seem to help.
Minimal Reproducible Example
Here’s the code for the UICollectionView
setup:
class ExpandableCell: UICollectionViewCell {
static let identifier = "ExpandableCell"
override init(frame: CGRect) {
backgroundColor = .lightGray
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hit = super.hitTest(point, with: event)
print(hit, hit?.isUserInteractionEnabled)
class ExpandableViewController: UIViewController {
private var selectedIndexPath: IndexPath?
private let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
return UICollectionView(frame: .zero, collectionViewLayout: layout)
override func viewDidLoad() {
view.addSubview(collectionView)
collectionView.frame = view.bounds
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(ExpandableCell.self, forCellWithReuseIdentifier: ExpandableCell.identifier)
extension ExpandableViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ExpandableCell.identifier, for: indexPath)
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedIndexPath = indexPath
UIView.animate(withDuration: 3, delay: 0, options: [.allowUserInteraction]) {
collectionView.collectionViewLayout.invalidateLayout()
collectionView.layoutIfNeeded()
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let isSelected = selectedIndexPath == indexPath
return .init(width: collectionView.bounds.width, height: isSelected ? 200 : 100)
ExpandableViewController()
<code>import UIKit
class ExpandableCell: UICollectionViewCell {
static let identifier = "ExpandableCell"
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .lightGray
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hit = super.hitTest(point, with: event)
print(hit, hit?.isUserInteractionEnabled)
return hit
}
}
class ExpandableViewController: UIViewController {
private var selectedIndexPath: IndexPath?
private let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
return UICollectionView(frame: .zero, collectionViewLayout: layout)
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
collectionView.frame = view.bounds
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(ExpandableCell.self, forCellWithReuseIdentifier: ExpandableCell.identifier)
}
}
extension ExpandableViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 20
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ExpandableCell.identifier, for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedIndexPath = indexPath
UIView.animate(withDuration: 3, delay: 0, options: [.allowUserInteraction]) {
collectionView.collectionViewLayout.invalidateLayout()
collectionView.layoutIfNeeded()
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let isSelected = selectedIndexPath == indexPath
return .init(width: collectionView.bounds.width, height: isSelected ? 200 : 100)
}
}
@available(iOS 17.0, *)
#Preview {
ExpandableViewController()
}
</code>
import UIKit
class ExpandableCell: UICollectionViewCell {
static let identifier = "ExpandableCell"
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .lightGray
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hit = super.hitTest(point, with: event)
print(hit, hit?.isUserInteractionEnabled)
return hit
}
}
class ExpandableViewController: UIViewController {
private var selectedIndexPath: IndexPath?
private let collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
return UICollectionView(frame: .zero, collectionViewLayout: layout)
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(collectionView)
collectionView.frame = view.bounds
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(ExpandableCell.self, forCellWithReuseIdentifier: ExpandableCell.identifier)
}
}
extension ExpandableViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 20
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ExpandableCell.identifier, for: indexPath)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
selectedIndexPath = indexPath
UIView.animate(withDuration: 3, delay: 0, options: [.allowUserInteraction]) {
collectionView.collectionViewLayout.invalidateLayout()
collectionView.layoutIfNeeded()
}
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let isSelected = selectedIndexPath == indexPath
return .init(width: collectionView.bounds.width, height: isSelected ? 200 : 100)
}
}
@available(iOS 17.0, *)
#Preview {
ExpandableViewController()
}
Observations
- I verified that all cells’
isUserInteractionEnabled
property is true
at all times.
- The scroll or touch inputs onto the cells that are not participating in the animations are handled correctly.
Question
Why are the cells not responding to touch events during the animation, despite using the .allowUserInteraction
option? How can I ensure that the cells remain responsive to touch events during the animation?
Any insights or suggestions would be greatly appreciated!