views:

452

answers:

2

I must be missing something obvious here but ...

UIControl has a method

- (void)addTarget:(id)target action:(SEL)action forControlEvents: (UIControlEvents)controlEvents

which lets you add an action to be called when any of the given controlEvents occur. ControlEvents are a bitmask of events which tell you if a touch went down, or up inside, or was dragged etc., there's about 16 of them, you or them together and get called when any of them occur.

The selector can have one of the following signatures

- (void)action
- (void)action:(id)sender
- (void)action:(id)sender forEvent:(UIEvent *)

none of those tell you what the control event bitmask was. The UIEvent is something slightly different, it's related to the actual touch event and doesn't (I think) contain the UIControlEvent. The sender (UIControl) doesn't have a way to find the control events either.

I'd like to have one method which deals with a number of control events as I have some common code regardless of which event or events happened but I still need to know what the UIControlEvents were for some specific processing.

Am I missing a way to find out what UIControlEvents were used when the action was called or do I really have to separate my code into

-(void)actionWithUIControlEventX;
-(void)actionWithUIControlEventY;
A: 

When you create your UIControl, set a value for the tag property. Then in your action function, you can determine the tag of the UIControl that called it using [sender tag]. Here's an example:

-(void)viewDidLoad {
    UIButton *button1 = [[UIButton alloc] initWithFrame(CGRectMake(0.0,0.0,100.0,100.0)];
    button1.tag = 42;
    [button1 addTarget:self action:@selector(actionWithUIControlEvent:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button1];


    UIButton *button2 = [[UIButton alloc] initWithFrame(CGRectMake(100.0,0.0,100.0,100.0)];
    button2.tag = 43;
    [button2 addTarget:self action:@selector(actionWithUIControlEvent:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button2];

}
-(void)actionWithUIControlEvent:(id)sender {
     switch([sender tag]) {
     case 42:
         //Respond to button 1
         break;
     case 43:
         //Respond to button 2
         break;
     default:
         break;
     }
 }
RichardM
A: 

I encountered the same problem, and came up with a solution. It's not amazingly pretty, but it works quite well. It is a UIControl category that stores the last UIControlEvent fired to its own tag property. You can get it from the link below. For further reference, here's the doc from my category, for a more detailed description of what's going on.

Hopefully this helps! Cheers/J

UIControl+CaptureUIControlEvents

git gist at: http://gist.github.com/513796

PROBLEM: upon firing, UIControlEvents are not passed into the target action assigned to the particular event. This would be useful in order to have only one action that switches based on the UIControlEvent fired.

SOLUTION: add a way to store the UIControlEvent triggered in the UIEvent.

PROBLEM: But we cannot override private APIs, so: (WORSE) SOLUTION: have the UIControl store the UIControlEvent last fired.

The UIControl documentation states that:

When a user touches the control in a way that corresponds to one or more specified events, UIControl sends itself sendActionsForControlEvents:. This results in UIControl sending the action to UIApplication in a sendAction:to:from:forEvent: message.

One would think that sendActionsForControlEvents: can be overridden (or subclassed) to store the flag, but it is not so. It seems that sendActionsForControlEvents: is mainly there for clients to trigger events programatically.

Instead, I had to set up a scheme that registers an action for each control event that one wants to track. I decided not to track all the events (or in all UIControls) for performance and ease of use.

USAGE EXAMPLE:

On UIControl setup:

UIControlEvents capture = UIControlEventTouchDown;
capture |= UIControlEventTouchDown;
capture |= UIControlEventTouchUpInside;
capture |= UIControlEventTouchUpOutside;
[myControl captureEvents:capture];
[myControl addTarget:self action:@selector(touch:) forControlEvents:capture];

And the target action:

- (void) touch:(UIControl *)sender {
  UIColor *color = [UIColor clearColor];
  switch (sender.tag) {
    case UIControlEventTouchDown: color = [UIColor redColor]; break;
    case UIControlEventTouchUpInside: color = [UIColor blueColor]; break;
    case UIControlEventTouchUpOutside: color = [UIColor redColor]; break;
  }
  sender.backgroundColor = color;
}
jbenet