Swift: Tvos Ibaction for UIcontrol in Collection View Cell Never Gets Called

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



Leave a reply



Submit