5

I want to implement a custom subclass of UIControl. It works beautifully except for one fatal problem that is making me spit teeth. Whenever I use sendActionsForControlEvents: to send an action message out, it omits to include a UIEvent. For example, if I link it to a method with the following signature:

- (IBAction) controlTouched:(id)sender withEvent:(UIEvent *)event

... the event always comes back as nil! The problem seems to occur within sendActionsForControlEvents:

Now, I need my IBAction to be able to determine the location of the touch. I usually do so by extracting the touches from the event. Surely there must be some way to ensure that the correct event is delivered? It's a pretty fundamental part of using a UIControl!

Anyone know a legal workaround?

Ash
  • 9,064
  • 3
  • 48
  • 59

2 Answers2

5

I would assume that this is because the sendActionsForControlEvents: method can't know which UIEvent (if any) your control event should be associated with.

You could try to send all the actions separately (replicating what the sendActionsForControlEvents: method does, according to the documentation), so you can specifically associate them with a UIEvent:

UIEvent *event = ...;
UIControlEvents controlEvent = ...;

for (id target in [self allTargets]) {
    NSArray *actions = [self actionsForTarget:target forControlEvent:controlEvent];
    for (NSString *action in actions) {
        [self sendAction:NSSelectorFromString(action) to:target forEvent:event];
    }
}
omz
  • 53,243
  • 5
  • 129
  • 141
  • I did consider this, but it suffers from the single horrible problem that you will find all targets receive all control events, regardless of what they have registered for. This is suboptimal. At the moment I'm using my own hacky code trick that I've posted above. – Ash Jul 08 '12 at 11:52
  • Ah hold on a minute, I think I see what you're getting at now. This does look like it has possibilities. – Ash Jul 08 '12 at 11:55
  • 1
    Yep, that works! I've plumbed it into a private method called sendActionForControlEvent:withEvent: and am using it in place of sendActionsForControlEvents: I am, frankly, amazed at how little information there is around the Internet regarding this oversight. – Ash Jul 08 '12 at 12:01
0

I have ONE possible solution at the moment, but I'm not very happy about it. For others faced with the same problem though, here it is. First, declare a local variable or property for a UIEvent thus:

@property (nonatomic, assign) UIEvent * currentEvent;

Now, in your touch-handling routines, set that local variable to the current UIEvent for that routine before calling [self sendActionsForControlEvents:] like so, replacing UIControlEventTouchDown with whichever action you want to send out of course.

self.currentEvent = event;
[self sendActionsForControlEvents: UIControlEventTouchDown];

Finally, override the following method thus:

- (void) sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
    [super sendAction:action to:target forEvent:self.currentEvent];
}

This works, but I am not in the least bit fond of it, so if anybody has an alternative solution that doesn't rely on holding a weak reference to a UIEvent, I will be overjoyed to hear it!

Ash
  • 9,064
  • 3
  • 48
  • 59