Adding context to a UI control or NSObject - objective-c

It's great to be able to addTarget on a UIButton. I only wish there was some sneaky way I could attach state to the UIButton so that when the target method is invoked, I could magically pull that state (any id) from the sender.
Something like:
[button shoveMyObjectInThere:foo];
[button addTarget:self action:#selector(touchyTouchy:) forControlEvents:UIControlEventTouchUpInside];
Followed by:
-(void) touchyTouchy:(id) sender {
UIButton button = (UIButton*)sender;
id foo = [button getByObjectBack];
// do something interesting with foo
}
Would be great if UIButton had an 'id context' property where developers could shove stuff, but that doesn't seem to be the case. Objective-C is a very dynamic language though, so I wonder if there is some sneaky way I can add method or fields to an object at runtime?

You could try making an associative reference
#import <objc/runtime.h>
objc_setAssociatedObject(id object, void *key, id value, objc_AssociationPolicy policy);
objc_getAssociatedObject(id object, void *key);

What about something like setValue:forKey:, a part of the Key-Value coding feature of Objective-C?

So I just did a quick test and found some interesting results.
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:#"Hello" forState:UIControlStateNormal];
[button addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
button.frame = CGRectMake(100, 100, 100, 100);
[self.window addSubview:button];
// ...
- (void)buttonClicked:(id)sender {
NSLog(#"button clicked %#", [sender class]);
}
This prints: button clicked UIRoundedRectButton
So it seems like this should be possible... Truth be told, I ran into some problems subclassing UIButton to get the full example working but this seems promising. :)

The official solution is to use the "tag" property:
[self.someMutableArray addObject:foo];
button.tag = self.someMutableArray.count - 1;
[button addTarget:self action:#selector(touchyTouchy:) forControlEvents:UIControlEventTouchUpInside];
Then:
-(void) touchyTouchy:(id) sender {
UIButton button = (UIButton*)sender;
id foo = self.someMutableArray[button.tag];
// do something interesting with foo
}
In most situations you'd use an enum or constant for the tag, but an array is obviously more flexible.

Related

need Clarification on NSButton Creation and Usage

I have below code to create simple NSButton in a separate function
-(void)myFunction
{
NSButton *btn = [self createButton:#"Button_Name"];
if(some condition )
{
[btn setEditable:YES];
}
}
- (NSButton*)createButton:(NSString *)buttonName
{
NSButton *btn = [[NSButton alloc] initWithFrame:NSMakeRect(20, 0, 20, 20)];
[btn setButtonType:NSSwitchButton];
[btn setImagePosition:NSImageOnly];
[btn setTarget:self];
[btn setTitle: buttonName];
return btn;
}
In my same It is working fine.I am using this code in a Big project.Will It work normally or will cause some problem.Is this a correct way?
Few things I would like to bring in your notice:
You pass buttonName and buttonTitle but never uses it.
You create an object of type NSButton but your object name is against the convention, by reading btnCell someone will expect it to be NSButtonCell.
In the above code I cant see any reference to the newly created button and even you are not adding it to any view. (I hope in your real Big project you are not missing those.)

Is it Possible to Change Action For UIButton?

I am trying to change Action For the UIButton in ios applicatio. I did Following Code
button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self action:#selector(aMethodShow:) forControlEvents:UIControlEventTouchDown];
[button setTitle:#"Show View" forState:UIControlStateNormal];
button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
[view addSubview:button];
In particular Section I want to change Action for this Button .So i did this
[button addTarget:self action:#selector(aMethodHide:) forControlEvents:UIControlEventTouchDown];
[button setTitle:#"Hide View" forState:UIControlStateNormal];
Unfortunately This code note Working?
I suggest before adding new target, first invoke removeTarget on UIbutton object then add new target with other action.
I think it will help you
[yourButton removeTarget:nil
action:NULL
forControlEvents:UIControlEventAllEvents];
[yourButton addTarget:self
action:#selector(yourAction:)
forControlEvents:UIControlEventTouchUpInside];
You can use same action target instead of using two target. In one target you have to differentiate like below
-(void)btnAction
{
if(target1)
{
// code for target 1
}
else
{
// code for target 2
}
}
Here target1 is BOOL value which value first set to be YES. And change its value NO whenever you want to perform target 2 code.
I hope this will helps You.
I made an app recently and i had the same situation, but i found another way to solve it so i decided to share my solution with people who may be in the same situation.
I'll try to explain what i did with the context of this question:
I added a tag to the button and i associated it with one of the functions that button needs to call (aMethodShow: for example).
button always call the same function (callSpecificFunc: for example). What callSpecificFunc: does is call either function aMethodShow: or aMethodHide according with the current button tag.
In the particular section in which the button needs to call a different function, i only change the tag of button.
Something like this:
NSInteger tagOne = 1000; //tag associated with 'aMethodShow' func
NSInteger tagTwo = 1001; //tag associated with 'aMethodHide' func
button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self action:#selector(callSpecificFunc:) forControlEvents:UIControlEventTouchDown];
[button setTitle:#"Show View" forState:UIControlStateNormal];
button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
[view addSubview:button];
...
// in some part of code, if we want to call 'aMethodShow' function,
// we set the button's tag like this
button.tag = tagOne
...
//Now, if we want to call 'aMethodHide', just change the button's tag
button.tag = tagTwo
...
-(void) callSpecificFunc:(UIButton*)sender
{
NSInteger tagOne = 1000;
NSInteger tagTwo = 1001;
if([sender tag] == tagOne){
//do whatever 'aMethodShow' does
}else {
//do whatever 'aMethodHide' does
}
}
Of course it could be applied for more than 2 functions :)

UIButton addTarget: self ... causes "Does not recognize selector" error

I initialised my UIButton-deriver liek this:
Button * it = [[Button alloc] initWithFrame:CGRectMake(x, y, image.size.width, image.size.height)];
Then, I do the next:
[(UIButton *)self addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[view addSubview:self];
The first line causes "Does not recognize selector" error.
Selector buttonClicked: looks like this:
-(IBAction) buttonClicked:(id)sender {
if (action) action();
else NSLog(#"Clicked.\n");
}
What am I doing wrong?
Add the action with:
[it addTarget:self
action:#selector(buttonClicked:)
forControlEvents:UIControlEventTouchUpInside];
and then add the button with
[view addSubview:it];
And don't create UIButtons with init... use the class method + buttonWithType:.
Be careful to to subclass UIButton, I am not sure if you really want this. Have a look at create uibutton subclass.
You create buttons with +(id)buttonWithType:(UIButtonType)buttonType

UIButton created runtime in a loop, now I need a specific function depending on the button touched

I am creating some buttons and putting them inside a UIScrollView like this:
int i = 0;
while (i != numberOfButtons ) {
int updatetY = 160*i;
CGRect buttonFrame = CGRectMake(updatetY, 0, 160, 60);
UIButton *button = [[UIButton alloc] initWithFrame:buttonFrame];
UIImage *buttonImage = [UIImage imageNamed:#"bg.png"];
[button setBackgroundImage:buttonImage forState:UIControlStateNormal];
[button setTitle:[buttonsArray objectAtIndex:i] forState:UIControlStateNormal];
[button addTarget:self action:#selector(MYPROBLEM) forControlEvents:UIControlEventTouchUpInside];
[menuScrollView addSubview:button];
i++;
}
Now I need to build a method to capture which button is being touched, and run a specific bunch of code (see (MYPROBLEM).
Anybody knows what I can do to "trace" which button is being touched and then run a specific function?
something like:
-(void) buttonfunction
{
case ...
doThis
case...
doThat
}
thanks in advance.
You need to use the tag property of the uibutton control. It's a numeric id for the button. You can set a tag on the button based on the i variable in your loop, for instance:
button.tag=i;
Then in your button's action message, you would simply add the id of the button as a parameter and check its tag like so:
-(void) buttonFunction:(id)sender {
switch (sender.tag)
{
//code goes here
}
}
Hope this helps.

How can I create a ToggleButton in Obj-C? Skin the UISwitch or subclass the UIButton? [duplicate]

Is there either a way to implement UISwitch with custom graphics for the switch-states?
Or as an alternative the other way round, an UIButton with UISwitch functionality?
UIButton already supports a "switch" functionality.
Just set a different image in Interface Builder for the "Selected State Configuration", and use the selected property of UIButton to toggle its state.
set the image to show on selected state:
[button setImage:[UIImage imageNamed:#"btn_graphics"] forState:UIControlStateSelected];
and then on touch up inside selector, set:
button.selected = YES;
if you want this to cancel another button's selection, set:
otherButton.selected = NO;
To build on what PGB and nurne said above, after you set your states and attach a selector (event method) you would want to put this code in that selector.
- (IBAction)cost:(id)sender
{
//Toggle current state and save
self.buttonTest.selected = !self.buttonTest.selected;
/**
The rest of your method goes here.
*/
}
For programmatically inclined:
-(void) addToggleButton {
CGRect aframe = CGRectMake(0,0,100,100);
UIImage *selectedImage = [UIImage imageNamed:#"selected"];
UIImage *unselectedImage = [UIImage imageNamed:#"unselected"];
self.toggleUIButton = [[UIButton alloc] initWithFrame:aframe];
[self.toggleUIButton setImage:unselectedImage forState:UIControlStateNormal];
[self.toggleUIButton setImage:selectedImage forState:UIControlStateSelected];
[self.toggleUIButton addTarget:self
action:#selector(clickToggle:)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.toggleUIButton];
}
-(void) clickToggle:(id) sender {
BOOL isSelected = [(UIButton *)sender isSelected];
[(UIButton *) sender setSelected:!isSelected];
}