How do I get a UIButton press to work in a UICollectionViewCell in Swift?

So this is a tvOS project in Swift. I have a custom UICollectionViewCell with a button as one of its subviews. I add a target to the button to allow it to interpret clicks. Here’s a simplified version of the relevant code

class CustomCell: UICollectionViewCell {
    var button:UIButton!

    override init(frame: CGRect) {
        super.init(frame: frame)

        button = UIButton(...) // Button is initialized with a frame
        button.userInteractionEnabled = true
        button.enabled = true
        button.addTarget(self, action: "pressed:", forControlEvents: .PrimaryActionTriggered)
        contentView.addSubview(button)
    }

    func pressed(sender: UIButton!) {
        print("button pressed!")
    }
}

For some reason it’s not ever printing out my message. I tried adding ‘pressedEnded’ to the cell class to see if it’s getting any and it gets called

  • Parse.com PFQuery order by time (Swift)
  • Simplify dictionary (xcode keeps indexing forever)
  • One-line closure without return type
  • in app purchase in SKScene
  • How to hide title/message frame in a UIAlertController?
  • Retrieve data from new Firebase
  • func pressesEnded(presses: Set<UIPress>, withEvent event: UIPressesEvent?) {
            // If I put a breakpoint here it's reached
    }
    

    Any suggestions? When I run it in the simulator the button can get focus, so I don’t know why it can’t get any events

    4 Solutions Collect From Internet About “How do I get a UIButton press to work in a UICollectionViewCell in Swift?”

    So, I figured out how to solve this, although it’s somewhat of a workaround. Basically for the UICollectionView I need to ensure the cell’s can’t get focus.

    Next I had didUpdateFocusInContext in CustomCell previously. This was what was actually animating the button when the cell, but when I checked the button never got focus. I’m guessing this was intercepting it. So I removed that function from CustomCell and instead, at the bottom of my file I added that function as an extension of UIButton.

    This also could’ve been done by creating a subclass of UIButton and using that instead, but this was less code (that is probably the ideal way). So the full code looks like:

    class MyCollection: UICollectionView, UICollectionViewDelegate {
        // Need initializer functions as well as functions for creating CustomCell's. They're omitted because they're not relevant to the answer
    
        func collectionView(collectionView: UICollectionView, canFocusItemAtIndexPath indexPath: NSIndexPath) -> Bool {
            return false
        }  
    }
    
    class CustomCell: UICollectionViewCell {
        var button:UIButton!
    
        override init(frame: CGRect) {
            super.init(frame: frame)
    
            button = UIButton(...) // Button is initialized with a frame
            button.userInteractionEnabled = true
            button.enabled = true
            button.addTarget(self, action: "pressed:", forControlEvents: .PrimaryActionTriggered)
            self.addSubview(button)
        }
    
        func pressed(sender: UIButton!) {
            print("button pressed!")
        }
    }
    
    extension UIButton {
        override public func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
            super.didUpdateFocusInContext(context, withAnimationCoordinator: coordinator)
    
            if self.superview is CustomCell {   // This ensures that all UIButtons aren't affected
                if context.nextFocusedView == self {
                    // Perform actions for when UIButton is focused
                }else {
                    // Perform actions for when UIButton loses focus
                }
            }
        }
    }
    

    The User Interaction Enabled checkbox is the key!

    Make sure the User Interaction Enabled checkbox is checked for the UICollectionViewCell. Then buttons actions will work as expected.

    enter image description here

    Above answer is correct, and if you are using a custom UICollectionViewCell you could also do this in its subclass for specific cells:

    override func canBecomeFocused() -> Bool {
        return false
    }
    

    It seems the UICollectionViewCell is overlayed automatically by a clear view, which catches taps.

    For us just calling bringSubview(toFront: checkButton) worked. Now the checkButton touchUpInside action is called as it should.

    import UIKit
    
    class SerieItemCollectionViewCell: UICollectionViewCell {
    
       var checked : (()->())?
    
    
       static let reuseIdentifier = "SerieItemCollectionViewCell"
    
       @IBOutlet weak var checkButton: UIButton!
       @IBOutlet var imageView: UIImageView!
    
       override func layoutSubviews() {
           super.layoutSubviews()
           bringSubview(toFront: checkButton)
       }
    
       @IBAction func buttonClicked(_ sender: Any) {
           checked?()
           print("Hello")
       }
    }