views:

473

answers:

3

I have a UIButton that selects itself on UIControlEventTouchUpInside. It deselects itself on UIControlEventTouchUpOutside. I also want it to deselect itself when there is a touch down outside of the button. Is there a good way to do this without subclassing UIWindow and overriding -hitTest:withEvent:?

EDIT: I just found this question, which confirms my fear that there isn't a really clean way to do this.

A: 

This is not how buttons are supposed to work. They cannot be active if there is no touch active inside them. What are you really trying to implement? Maybe a UIBUtton is not the best choice here.

St3fan
By "selected," I meant it adds `UIControlStateSelected` to its controlState. This is how a `UISwitch` stores its on/off state. My button is used for in-app purchasing, and thus behaves like Apple's buy buttons in iTunes or the App Store. The first tap up inside changes the button's control state, and the second one performs an action.
lemnar
You really should not use the button state to get that specific behaviour. I think it is much simpler to add your own state to the button in an external variable or maybe even in a subclass. Like `ShowingPrice` and `ShowingBuyNow`.
St3fan
I will likely turn the button into a subclass, but I don't understand why it would be inappropriate to keep information about its state in the `controlState` property. Perhaps the `UIControlStateApplication` bits are more appropriate than `UIControlStateSelected`, but I'm not even convinced of that. By using `controlState`, I get things for free, like the changing of the `backgroundImage`, the `title`, and the `titleColor` based on the state of the button.
lemnar
A: 

You can do this with your UIViewController by implementing the touchesBegan/Moved/Ended methods. If they detect a touch outside of the button, set its state back to normal. Take a look at Event Handling.

David Kanarek
That's better than subclassing `UIWindow`, but I don't think it will do what I want. The button is in a `UITableViewCell` in a `UITableView` in a `UINavigationController`'s view in a `UITabBarController`'s view. A `-touchesBegan:withEvent:` method would have to be implemented higher up in the view hierarchy if it's going to "eat" a tap down on a tab bar button. That's exactly what I'm trying to avoid. My `UITabBarController` shouldn't need to know about the state of a button five levels down the view hierarchy. I'd like the button itself to get a tap from anywhere in the window's frame.
lemnar
I don't use it very often, so I have no idea if this could help, but you could try becomeFirstResponder on the button when it's selected. Again, this might have no effect, but I can't think of anything else. Apple love to keep some nice features to themselves.
David Kanarek
Unfortunately, the first responder is only first for non-touch events. Touch events travel down the view hierarchy and then up the responder chain. The `exclusiveTouch` property, however, sounds promising. I dismissed it earlier because the event handling documentation suggests that it won't affect touches outside the frame of the view.
lemnar
A: 

I solved this, and released the source. I didn't have to subclass UIWindow, but I did need to override -hitTest:withEvent:.

lemnar
Your project fails to build: pbxcp: DCSKit.bundle: No such file or directory
DenNukem
The project now builds.
lemnar