Search code examples
swiftuibuttonuicollectionviewcelltvosuicontrolevents

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

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


Solution

  • 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
                }
            }
        }
    }