When using a TVCardView
in a UICollectionViewCell
the UICollectionViewDelegate
contextMenuConfigurationForItemsAt
function is not called.
Any idea if it's possible to work around this? I don't want to lose the default focus size increase effect of the standard card view, but I also would like to add support for context menus (now that they're finally available in tvOS 17)
Full reproducible example:
import UIKit
import TVUIKit
class ViewController: UICollectionViewController {
private var dataSource: UICollectionViewDiffableDataSource<String, String>?
override func viewDidLoad() {
super.viewDidLoad()
let tvCardCellRegistration = UICollectionView.CellRegistration<TVCardCell, String> { cell, _, _ in
}
let regularCellRegistration = UICollectionView.CellRegistration<RegularCell, String> { cell, _, _ in
cell.contentView.backgroundColor = .orange
}
let dataSource = UICollectionViewDiffableDataSource<String, String>(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in
if indexPath.section == 0 {
collectionView.dequeueConfiguredReusableCell(
using: tvCardCellRegistration,
for: indexPath,
item: itemIdentifier
)
} else {
collectionView.dequeueConfiguredReusableCell(
using: regularCellRegistration,
for: indexPath,
item: itemIdentifier
)
}
}
self.dataSource = dataSource
let collectionViewLayoutConfiguration = UICollectionViewCompositionalLayoutConfiguration()
collectionViewLayoutConfiguration.interSectionSpacing = 40
collectionView.delegate = self
collectionView.setCollectionViewLayout(UICollectionViewCompositionalLayout(
sectionProvider: { _, _ in
let layoutSize = NSCollectionLayoutSize(
widthDimension: .absolute(220),
heightDimension: .absolute(220)
)
let group = NSCollectionLayoutGroup.horizontal(
layoutSize: layoutSize,
subitems: [
NSCollectionLayoutItem(layoutSize: layoutSize)
]
)
let layout = NSCollectionLayoutSection(group: group)
layout.orthogonalScrollingBehavior = .continuousGroupLeadingBoundary
layout.contentInsetsReference = .none
layout.interGroupSpacing = 20
layout.contentInsets = .init(top: 12, leading: 60, bottom: 0, trailing: 60)
return layout
},
configuration: collectionViewLayoutConfiguration
), animated: false)
var snapshot = NSDiffableDataSourceSnapshot<String, String>()
snapshot.appendSections(["section1", "section2"])
snapshot.appendItems(["1", "2", "3", "4", "5"], toSection: "section1")
snapshot.appendItems(["a", "b", "c", "d", "e"], toSection: "section2")
dataSource.apply(snapshot, animatingDifferences: false)
}
@available(tvOS 17.0, *)
override func collectionView(
_ collectionView: UICollectionView,
contextMenuConfigurationForItemsAt indexPaths: [IndexPath],
point: CGPoint
) -> UIContextMenuConfiguration? {
UIContextMenuConfiguration(actionProvider: { _ in
let deleteAction = UIAction(
title: "Delete",
image: UIImage(systemName: "trash"),
attributes: .destructive
) { _ in
//
}
return UIMenu(title: "", children: [deleteAction])
})
}
}
class TVCardCell: UICollectionViewCell {
let cardView = TVCardView()
override init(frame: CGRect) {
super.init(frame: frame)
cardView.cardBackgroundColor = .magenta
cardView.contentSize = CGSize(
width: 220,
height: 220
)
contentView.addSubview(cardView)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
cardView.frame = contentView.bounds
}
}
class RegularCell: UICollectionViewCell {}
So turns out if you add the TVCardView
directly to the UICollectionViewCell
rather than the contentView
as you would normally do (and should, according to Apple), the context menu works fine.
e.g.
class TVCardCell: UICollectionViewCell {
let cardView = TVCardView()
override init(frame: CGRect) {
super.init(frame: frame)
cardView.cardBackgroundColor = .magenta
cardView.contentSize = CGSize(
width: 220,
height: 220
)
addSubview(cardView) // don't add to contentView here
}
...