Search code examples
iosswiftuiviewuibuttonstoryboard

iOS: Why does hidden button still receive tap events?


According to the Apple docs, hidden UIButtons should not receive tap events.

However, our app has a UIButton receiving tap events despite being hidden.

This function is the IB Action invoked when the button is tapped. When the button is removed from Storyboard, this function doesn't get invoked. When the button is added to Storyboard, the function gets invoked -- even though the button is hidden.

To verify that the button is hidden, we put a breakpoint inside the function and ran expr sender.hidden from the Xcode debugger. The result: true.

The stack trace shows the IB Action is triggered by code in UIApplicationMain, not our code.

Through the Connections Inspector, we confirmed there is no other trigger for the IB Action except the mysterious button.

Thoroughly confused. Suggestions?

@IBAction func buttonTapped(sender: UIButton) {        
    // If here, handle tap
    ...
}

Solution

  • The problem was an incomplete UIButton extension that didn't account for visibility in determining hit tests.

    This function correctly handles the case where UIButtons are hidden.

    extension UIButton {
        public override func hitTest(point: CGPoint, withEvent event: UIEvent?) -> UIView? {
            // Ignore if button hidden
            if self.hidden {
                return nil
            }
    
            // If here, button visible so expand hit area
            let hitSize = CGFloat(56.0)
            let buttonSize = self.frame.size
            let widthToAdd = (hitSize - buttonSize.width > 0) ? hitSize - buttonSize.width : 0
            let heightToAdd = (hitSize - buttonSize.height > 0) ? hitSize - buttonSize.height : 0
            let largerFrame = CGRect(x: 0-(widthToAdd/2), y: 0-(heightToAdd/2), width: buttonSize.width+widthToAdd, height: buttonSize.height+heightToAdd)
            return (CGRectContainsPoint(largerFrame, point)) ? self : nil
        }
    }