Anthony Main
Anthony Main

Reputation: 6068

How do I assign a name to a control and retrieve it in an event method?

I have a IBAction such as:

- (IBAction)showPicker:(id)sender;

How can I get the name of the control from the sender variable?

I am typically a c# coder so have tried the following to no avail

senderName = ((UIButton *)sender).name;

I need something more descriptive than the control id (not the button title either). I have 5 buttons all calling the same method. I need to determine which was clicked in order to perform the methods actions on the appropriate control. I.E I have an address picker method but want to populate 5 text fields with different details with each of 5 buttons. Just trying to keep the code tidy

N.B Originally I was planning on using the Interface Builders name field, but I've been advised (below) that this isn't available at runtime.

Upvotes: 2

Views: 5610

Answers (6)

JasonD
JasonD

Reputation: 7652

While I agree that tag will work in many circumstances, it's a pain to work with integer tags when you have a matrix of 100 buttons that are pushable where you want to identify: (1) what was pushed and (2) what value was pushed. I found myself starting to invent row/column naming schemes with tag numbers which got out of hand.

I wanted multiple controls to share the same handler, and I wanted to carefully design my UI in IB to pass name/value pairs off to a key-value store based on what was pushed.

As an alternative to tags, I came up with another workaround for UISegmentedControl (it works for other controls too).

(1) Create a new class and sub-class UISegmentedControl. Add a string property MyCustomKey to your new class.

@interface CustomSegmentedControl : UISegmentedControl
@property (nonatomic, retain) NSString* myCustomKey; 
@end

(2) Back in IB, modify your UISegmentedContol instances to be instances of CustomSegmentedControl. Used the IB "User Defined Runtime Attributes" to set the value of myCustomKey using IB. KeyValueCoding will take care of the setting of the properties for you:

screen shot of setting your custom property through interface building

(3) Wire up all your buttons to a single IBAction.

(4) In your IBAction, you can extract the key-value pairs now by reading back your custom attribute:

- (IBAction)myButtonPressed:(id) sender
{
    if ([sender isKindOfClass:[CustomSegmentedControl class]] == false)
        return;

    // get the key-values from the control
    CustomSegmentedControl *control = (CustomSegmentedControl *)sender;
    NSString *key = [control valueForKey:@"myCustomKey"];
    NSNumber *value = [NSNumber numberWithInteger:[control selectedSegmentIndex]];
    NSLog(@"Pressed control %@ with a value of %@", key, value);
}

In this case I was only interested in the integer value of a segmented control, but you could get any property off the control as the value.

Hope this helps.

Upvotes: 3

Chris Hanson
Chris Hanson

Reputation: 55164

You don't use the name of an object in Interface Builder to refer to that object; it's just there for reference. (Interface Builder will also use it as a hint for what outlets connecting to the control and actions sent by the control might be named, but only as a hint.)

Instead, you add an outlet to a controller object that you connect to the object. This lets you access the object directly at runtime from your controller without going through any look-up step.

Also, it sounds like you have five buttons, with five things to do, all connected to the same action method. Don't do that.

In Cocoa and Cocoa Touch, you shouldn't typically create an action methods "per event" and then decide what to do based on which control sent the event. The controls in Cocoa handle events themselves, and translate them into higher-level actions. So you can implement five action methods in your controller, connect each button to its appropriate action method, and never have to decide what to do based on which control sent the action.

Obviously this isn't appropriate in all cases. For example, if you have a matrix of buttons with images that change at runtime, you would probably want them all to connect to the same action and distinguish them based on their tag property. A good rule of thumb is to use different action methods when dealing with "different" things, and use the same action method and control tags when dealing with "many of the same" thing.

Upvotes: 3

Stephen Darlington
Stephen Darlington

Reputation: 52565

You might want to look at using the tag property. It's an integer rather than a name, but can be used to differentiate between two controls. It's inherited from UIView so any control that's sending an event should have it.

It's editable in Interface Builder under View attributes.

It's a property so it can be accessed programatically with the dot notation:

control.tag

Or you can convert to a string as follows:

[stringWithFormat:@"%d", control.tag]

Upvotes: 5

Abizern
Abizern

Reputation: 150665

Try:

senderTitle = [sender currentTitle];

Upvotes: 0

Marc Charbonneau
Marc Charbonneau

Reputation: 40515

The name field under Interface Builder Identity is something unique to Interface Builder; I'm actually not sure exactly how it's used, but it doesn't map to a usable property, ivar or method on your actual control. Instead, use the tag property (under the View attributes) to differentiate different controls from an action method if you don't otherwise keep a reference to them as an ivar. If I'm misunderstanding you and you want the button's title, just use currentTitle instead.

Upvotes: 2

Matthew Schinckel
Matthew Schinckel

Reputation: 35629

If the control has a name attribute, then:

NSString *senderName = [sender name];

This isn't something most controls have. You might want:

NSString *senderName = [sender title];

If it was an NSButton, for instance. Or as Marc says, for a UIButton, use currentTitle.

Upvotes: 0

Related Questions