HelloMoon
HelloMoon

Reputation:

How to implement target-action-mechanism for custom control?

I'm going to write my own custom control that is very different from UIButton. It's so much different that I decided to write it from scratch. So all I subclass is UIControl.

When my control is touched up inside, then I want to fire a message in means of target-action. The user of that class may instantiate it and then add some targets and actions for this event.

i.e. imagine I would call internally a method -fireTargetsForTouchUpEvent. How could I maintain this target-action-mechanism in my class? Do I have to add all targets and actions to my own array and then just call selectors (the actions) on the target objects in a for-loop? Or is there a more intelligent way to do it?

I imagine to provide some methods for adding targets and actions for some events like that touch up event (I raise that manually by calling a internal method when that happens). Any idea?

Upvotes: 16

Views: 9135

Answers (3)

pixelfreak
pixelfreak

Reputation: 17844

I just want to clarify what @Felixyz said because it wasn't clear to me at first.

If you are subclassing UIControl, even if you are going to have a custom event, you don't have to keep track of your own targets/actions. The functionality is already there, all you have to do is call the code below in your subclass to trigger the event:

[self sendActionsForControlEvents:UIControlEventValueChanged];

Then in the view or view controller that instantiates your custom UIControl, just do

[customControl addTarget:self action:@selector(whatever) forControlEvents:UIControlEventValueChanged];

For custom event, just define your own enum (for example, UIControlEventValueChanged is equal to 1 << 12). Just make sure it is within the permitted range defined by UIControlEventApplicationReserved

Upvotes: 39

James Eichele
James Eichele

Reputation: 119214

You have the right idea. Here is how I would do it:

@interface TargetActionPair : NSObject
{
    id target;
    SEL action;
}
@property (assign) id target;
@property (assign) SEL action;
+ (TargetActionPair *)pairWithTarget:(id)aTarget andAction:(SEL)selector;
- (void)fire;
@end

@implementation TargetActionPair
@synthesize target;
@synthesize action;

+ (TargetActionPair *)pairWithTarget:(id)aTarget andAction:(SEL)anAction
{
    TargetActionPair * newSelf = [[self alloc] init];
    [newSelf setTarget:aTarget];
    [newSelf setAction:anAction];
    return [newSelf autorelease];
}

- (void)fire
{
    [target performSelector:action];
}

@end

With that class in place, storing your target/action pairs is pretty straightforward:

MyCustomControl.h:

#import "TargetActionPair.h"

@interface MyCustomControl : UIControl
{
    NSMutableArray * touchUpEventHandlers;
}

- (id)init;
- (void)dealloc;

- (void)addHandlerForTouchUp:(TargetActionPair *)handler;

@end

MyCustomControl.m:

#import "TargetActionPair.h"

@implementation MyCustomControl

- (id)init
{
    if ((self = [super init]) == nil) { return nil; }
    touchUpEventHandlers = [[NSMutableArray alloc] initWithCapacity:0];
    return self;
}

- (void)dealloc
{
    [touchUpEventHandlers release];
}

- (void)addHandlerForTouchUp:(TargetActionPair *)handler
{
    [touchUpEventHandlers addObject:handler];
}

- (void) fireTargetsForTouchUpEvent
{
    [touchUpEventHandlers makeObjectsPerformSelector:@selector(fire)];
}

@end

After that, setting up the control would be done as follows:

[instanceOfMyControl addHandlerForTouchUp:
         [TargetActionPair pairWithTarget:someController
                                andAction:@selector(touchUpEvent)];

Upvotes: 15

Felixyz
Felixyz

Reputation: 19143

Since you're planning to subclass UIControl, you can just use

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

Using this, any class can register itself as a target for any events it wants to on your custom controller.

Upvotes: 5

Related Questions