Swift: tvOS IBAction for UIControl in Collection View cell never gets called
I have finally found it. As usual it's due to tvOS focus. Basically the cell has to be not focusable, so that the UIControl element inside the cell (TVPosterView in my case, but it could be any UIControl) will be the focused one. It's tricking because graphically it does appear as if the poster had focus (one can rotate the poster a bit around).
The solution is to add collectionView(_:canFocusItemAt:) to the UICollectionViewDelegate
extension SecondViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView,
canFocusItemAt indexPath: IndexPath) -> Bool {
return false
}
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
// Now, thanks to the function above, this is disabled
print("From didSelectItemAt indexPath: \(indexPath.item + 1)")
}
}
I have tested it in the code of this question and added it also to my project, which finally works!
Popover attached to cell in UICollectionView moving when is called the ReloadData of this collection view
Instead of attaching the popover to a cell who you can guarantee won't be dequeued, you could use a temporary UIView created from the cell's frame and then attach the popover to that. Here's the relevant code to accomplish this.
class ViewController: UIViewController {
var timer = Timer()
var popOverTempView = UIView()
@IBOutlet weak var collectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(reloadData), userInfo: nil, repeats: true)
self.collectionView.addSubview(popOverTempView)
popOverTempView.isHidden = true
popOverTempView.backgroundColor = .clear
}
}
extension ViewController: UICollectionViewDelegate {
func presentPopover(from view: UIViewController, cell: UIView) {
let popoverView = PopoverViewController(nibName: "PopoverViewController", bundle: nil)
let popover: UIPopoverPresentationController = popoverView.popoverPresentationController!
popoverView.delegate = self
popOverTempView.frame = cell.frame
popOverTempView.isHidden = false
popover.sourceRect = popOverTempView.bounds
popover.sourceView = popOverTempView
view.present(popoverView, animated: true, completion: nil)
}
}
extension ViewController: PopoverViewControllerDelegate {
func willDismissPopover() {
popOverTempView.isHidden = true
}
}
protocol PopoverViewControllerDelegate {
func willDismissPopover()
}
class PopoverViewController: UIViewController {
var delegate: PopoverViewControllerDelegate?
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
delegate?.willDismissPopover()
}
}
Update 5/20/20:
I think I found a cleaner more direct fix. Instead of using a cell's bounds directly, convert it's contentView's frame to the collectionView's coordinate space, then use that as the sourceRect and present from the current view controller's view.
func presentPopover(from view: UIViewController, cell: UICollectionViewCell, indexPath: IndexPath) {
let popoverView = PopoverViewController(nibName: "PopoverViewController", bundle: nil)
let popover: UIPopoverPresentationController = popoverView.popoverPresentationController!
var rect = cell.convert(cell.contentView.frame, to: collectionView)
rect = collectionView.convert(rect, to: self.view)
popover.sourceRect = rect
popover.sourceView = self.view
view.present(popoverView, animated: true, completion: nil)
}
tvOS/Swift 3: How fix broken UICollectionView selection animation?
After a fresh crack at the problem this morning, here's what works.
In the UICollectionViewCell
class CollectionViewCellA: UICollectionViewCell {
@IBOutlet var image: UIImageView!
@IBOutlet var bkgImage: UIImageView!
@IBOutlet var title: UILabel!
var selectTrans: UIFocusAnimationCoordinator?
var scale : CGFloat = 0.0
override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
struct wrapper {
static let s_atvMotionEffect = UIAppleTVMotionEffectGroup()
}
coordinator.addCoordinatedAnimations( {
if self.isFocused {
self.addMotionEffect(wrapper.s_atvMotionEffect)
self.layer.shadowOpacity = 0.75;
self.layer.shadowRadius = 10.0;
self.layer.shadowOffset = CGSize(width: 0, height: 5);
self.scale = 1.2
let transform = CGAffineTransform(scaleX: self.scale, y: self.scale)
self.layer.setAffineTransform(transform)
} else {
self.removeMotionEffect(wrapper.s_atvMotionEffect)
self.layer.shadowOpacity = 0.25;
self.layer.shadowRadius = 4.0;
self.layer.shadowOffset = CGSize(width: 0, height: 0);
self.scale = 1.0
let transform = CGAffineTransform(scaleX: self.scale, y: self.scale)
self.layer.setAffineTransform(transform)
}
},completion: nil)
}
func startSelectAnimation() {
scale = 1.1
let transform = CGAffineTransform(scaleX: scale, y: scale)
self.layer.setAffineTransform(transform)
perform(#selector(middleAnimation), with: nil, afterDelay: 0.08)
}
func middleAnimation() {
scale = 0.95
let transform = CGAffineTransform(scaleX: scale, y: scale)
self.layer.setAffineTransform(transform)
perform(#selector(endSelectAnimation), with: nil, afterDelay: 0.1)
}
func endSelectAnimation(){
scale = 1.2
let transform = CGAffineTransform(scaleX: scale, y: scale)
self.layer.setAffineTransform(transform)
}
}
In the ViewController
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// We have to provide animation, so get the cell and do call to animation
let selectedCell = collectionView.cellForItem(at: indexPath) as! CollectionViewCellA
selectedCell.startSelectAnimation()
if collectionView == self.freeContentCV {
performSegue(withIdentifier: "ShowVideoDetail", sender: self)
}
}
The animation is a little clunky, but when it triggers a segue the jerkiness is not all that noticeable.
How do I get ahold of my cell when clicking in collectionView using rx?
Using whichever variable you assigned to your collection view, I normally access it using something like this in my viewDidLoad
:
myCollectionView.rx.itemSelected
.subscribe(onNext: { [weak self] indexPath in
let cell = self?.myCollectionView.cellForItem(at: indexPath)
// Perform your animation operations here
}).diposed(by: disposeBag)
Based on your example, you can use the same idea and access your cell using the cellForItem(at:)
function:
Observable.zip(myCollectionView.rx.itemSelected, myCollectionView.rx.modelSelected(RecipesCollectionViewCellViewModel.self))
.bind { indexPath, model in
let cell = self?.myCollectionView.cellForItem(at: indexPath) as? MyCollectionViewCell
}.disposed(by: disposeBag)
How to add uibutton action in a collection view cell?
May be needful for you-
Write this code in cellForItemAtIndexPath
Swift 2.X
let editButton = UIButton(frame: CGRectMake(0, 20, 40,40))
editButton.setImage(UIImage(named: "editButton.png"), forState: UIControlState.Normal)
editButton.addTarget(self, action: #selector(editButtonTapped), forControlEvents: UIControlEvents.TouchUpInside)
cell.addSubview(editButton)
Swift 3.X
let editButton = UIButton(frame: CGRect(x:0, y:20, width:40,height:40))
editButton.setImage(UIImage(named: "editButton.png"), for: UIControlState.normal)
editButton.addTarget(self, action: #selector(editButtonTapped), for: UIControlEvents.touchUpInside)
cell.addSubview(editButton)
And perform your action-
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func editButtonTapped() -> Void {
print("Hello Edit Button")
}
Related Topics
Swift Xcode 7 Beta 5 Type Cannot Refer to Itself as a Requirement
Swift 4 "Extra Argument in Call" Rxswift
Getting Reference to a Dictionary Value
Extract Reality Composer Scene for Arquicklook
How to Convert String to Date Without Time in Swift 3
Scenekit Ar Game Fps Getting Low and The Device Getting Hot with Use
Using Auto Layout to Orientate Stack Views Vertically in Portrait and Horizontally in Landscape
How to Stretch a View to Its Parent Frame with Swiftui
Kvo Listener Issues in Swift 4
How to Reconnect Akplayer and Akmixer After Audiokit.Stop()
Testing a Class Which Preserves Its State in Private Variables
Access Each Header and Controls in The Tableview in Swift
Swiftui New App Lifecycle How to Connect The Facebook Sdk
Swift Loaditem Closure Not Running
How to Get a Full List of Running Processes