Determine which UIButton fired event in Objective C - ios

I have a handful of UIButtons that when pressed fire the method, (IBAction)buttonPressed:(id)sender. Right now I have a document label set for each (btnPlay, btnStop, btnPause), but I don't believe I can access this in Objective C. Is there something I can set in xcode that acts as a variable so when buttonPressed() is fired I know which button (btnPlay, btnStop, or btnPause) fired the event?

You should change your IBAction to something like the below
-(IBAction)buttonPressed:(UIButton *)button {
if([button.titleLabel.text isEqualToString:#"Start"]){
//Do Stuff
}
}
In this way you can access the sender as a button directly with no issues or type casting required, you can then use the isEqualToString method to check the title of the button and run code inside the if statement.
You might also like to consider using the tag property which pretty much all Interface Objects have.
if(button.tag == 1){
//Do Stuff
}
Switch statements are also a nice clean way of handling different events..
switch (button.tag) {
case 1:
// Do Something
break;
default:
// Do Default Action
break;
}

you can define which method has to be called when the button pressed after #selector in this case playVideo method.
[videoButton setTitle:#"play video" forState:UIControlStateNormal];
[videoButton setBackgroundImage:nil forState:UIControlStateNormal];
[videoButton addTarget:self action:#selector(playVideo:)forControlEvents:UIControlEventTouchUpInside];

That's what the sender argument is there for - you can compare it against each of your buttons in a chain of if statements to see which one sent that message.

Every UIButton has a titleLabel property, which is a UILabel. Check sender.titleLabel.text and compare it against the three strings.
Alternatively, you can also assign each button a tag (generally an integer), either through the Attributes Inspector in Xcode, or using the tag property in code. Then check sender.tag in your action method.

Related

UIButton created from an array of strings needs a target

Here it is: I am working with some legacy code. So I can't really (feasibly) change the architecture at this point.
I have one file that creates an array of strings, in VC1:
NSMutableArray *arrButtons = [NSMutableArray array];
[arrButtons addObject:data];
[arrButtons addObject:share];
[VC2 showButtons:arrButtons];
Then on my VC2 code , I have:
-(void)showButtons:(NSMutableArray *)arrButtons {
for (int i=0; i<arrButtons.count; i++) {
UIButton *btn = [_popupView viewWithTag:i+5000];
[btn setTitle:[arrButtons objectAtIndex:i] forState:UIControlStateNormal];
//this is the code I am trying out, I just need to addtarget to data, not the rest of the array.
if ([arrButtons containsObject:data]) {
//this is adding to all buttons, not just data. Figure out a way to add this action to only data.
btn.[index: data]
[btn addTarget:self action:#selector(arrayButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
}
}
It would make sense to just add the target in VC1 when we first add it to the array. BUT I can't because when it is created, it is just a string.
I'd like to point out that the button DOES show up on screen. But I don't know how to access that specific button in the array to add a target to it.
The best solution I can come up with is that I need to addTarget but if anyone else has any pointers or ideas on how something like this can be resolved, I would really appreciate it.
p.s. I know how to connect IBActions from IB, the problem is this is a button created 100% programmatically, and when created, it is really just a string, not a button. So addTarget is not available.
I think that the problem in VC2 is in:
if ([arrButtons containsObject:data]) {
If I'm understanding this correctly, arrButons will always contain the data string. Assuming that data is the variable representing title of the button you want to add a target to, I'd put this instead:
if ([arrButtons[i] isEqualToString: data]) {
Please let us know how it goes.

How to dynamically assign method to button

In my IOS app I have a button that the user can assign a custom action to, but I don't know what the best way is to dynamically set actions to the button. My first idea was to change the [sender tag] of the button, and then after being pressed it will read a series of "if" statements that say "if (sender tag=1){then do this} if(sender tag=2){then do this}, and so on." But the problem with this is that i will have thousands of possible actions, so if someone chooses an action who's sender tag is 2000, then the method will have to read through every single question before it reads the "if (sender tag=2000)", which is a waste of time. How can I dynamically set an action to a Button?
You have lots of options. Buttons support target/action, so you can change the button's action using
removeTarget:action:forControlEvents: and addTarget:action:forControlEvents:.
Or you could use tags as you say. Tags are numeric, so you could use a switch statement rather than a chain of if/then/else. Better yet, though, would be to use an NSArray of blocks and invoke the appropriate block for the user-selected action.
Example:
In your header:
typedef void (^myButtonBlock)(id sender);
In your implementation:
myButtonBlock aBlock = ^(UIButton *sender)
{
NSLog(#"Button block triggered for button with tag %d", sender.tag);
}
NSMutableArray *blocksArray = [NSMutableArray new];
[blocksArray addObject: aMethod];

Using IF statement to test the ON/OFF setting of iOS Switch Control

I am new to Objective-C, but use JavaScript and a lot of VB.NET and some C for firmware development. I am writing beginner level Apps and in this case, have a SWITCH control on my View. I want to know how to use an IF statement to test it's ON/OFF state. Sample code is best as I am legally blind and have difficulty reading lots of text online.
Same issue for the Stepper control (The control with the +/_ buttons used to increment / decrement values.)
Thanks.
UISwitch does have a property on (check the docu here: UISwitch class reference) so check the property (note that the getter is named differently (isOn)):
#property(nonatomic, getter=isOn) BOOL on
So one would check the switch like this:
if ([switch isOn])
{}
else
{}
first add a target to the switch for event UIControlEventValueChanged after adding the switch to your view, then use the switch's property to determine its state,
[mySwitch addTarget:self action:#selector(switchToggled:) forControlEvents:UIControlEventValueChanged];
- (void) switchToggled:(id)sender {
UISwitch *mySwitch = (UISwitch *)sender;
if ([mySwitch isOn]) {
NSLog(#"its on!");
} else {
NSLog(#"its off!");
}
}

Passing id to button action method in Objective-C

I have UIButtons programatically created. Now, I created an method to trigger for the button like so:
-(void)createButton {
//code to create button
[mybutton addTarget:self action:#selector(myAction:)forState:UIControlStateNormal];
}
-(void)myAction:(id)sender {
if([tag sender] == 0) {
posX = 380;
} else if(....... //set posX to different values
}
[self.myScroll setContentOffset:CGPointMake(poxX, 0) animated:YES];
That's pretty much what the buttons do aside from loading data. Basically, I am using the buttons as tabs. If I tap on a button, it slides to the center. In one of these buttons, there's an "update buttons" button where I can add and remove more buttons. If I tap on one of the buttons, it would automatically be removed and if I tap add, one would automatically add. There's no problem with that. The thing is, I want to retain the "update buttons" button centered as it is technically still the selected button. Here's how the method inside the view for update buttons:
-(void)updateButtons {
NSUInteger index = [self.anArray indexOfObject:#"btnChange"];
id indexId = [NSNumber numberWithInteger: index];
//this following line causes the app to crash because it does not recognize the indexId I'm trying to set
[self myAction:indexId];
}
Everytime I execute the updateButtons function and myAction is triggered, the app crashes with an uncaught exception. So my question is, how can I properly pass an id to an action method?
Precise answer to your question is: pass nil for the sender parameter:
[self myAction:nil];
-(void)createButton
{
//code to create button
myButton.tag = 1;
[mybutton addTarget:self action:#selector(myAction:)forState:UIControlStateNormal];
}
-(void)myAction:(id)sender {
//do something here
}
you are trying to pass the NSNumber object but you need to pass the UIButton object to myAction: method, I think you should create the UIButton object in the .h file and add tag to the button and pass the reference of that button object to the method
-(void)updateButtons
{
NSUInteger index = [self.anArray indexOfObject:#"btnChange"];
if(myButton.tag == index){
[self myAction:myButton];
}
}
-(void)updateButtons {
NSUInteger index = [self.anArray indexOfObject:#"btnChange"];
mybutton.tag=index
[self myAction:nil];
}
then in
-(void)myAction:(id)sender {
//do something here
int index=[sender tag]; //this is your index
}
id in Objective-C just means any object—it's used to avoid specifying a certain type of object. When using target-action, the first argument is the sender, or the object that sent the action. For the buttons, the sender would be an instance of UIButton.
I would recommend adding NSLog(#"%#",sender); to your action method to see what type it is each time the method is called.
So what should be passed as the argument? Well, it depends on what myAction does, and you'll have to share that code to get more details on this. If myAction doesn't use the sender argument, you can safely pass nil as other answerers suggest.
Note that when you use target-action, if you're not using the sender argument, you can leave it off altogether. Just declare your method like this:
-(void)myAction
{
// code here
}
[mybutton addTarget:self action:#selector(myAction:)forState:UIControlStateNormal];
Instead of above, replace this below line
[mybutton addTarget:self action:#selector(myAction:) forControlEvents:UIControlEventTouchUpInside];
Try this:
-(void)createButton
{
//code to create button
[mybutton addTarget:self action:#selector(myAction:)forState:UIControlStateNormal];
}
-(void)myAction:(id)sender
{
//do something here
}
-(void)updateButtons
{
NSUInteger index = [self.anArray indexOfObject:#"btnChange"];
id indexId = [NSNumber numberWithInteger: index];
[self myAction:mybutton];//if u r using my button else u can use
[self myAction:nil]
}
First off, seems weird that you are using -addTarget:action:forState. UIButton is a concrete subclass of UIControl and as such, instead uses the method -addTarget:action:forControlEvents:
which can take a variable number of or-able UIControlEvent enums. Specifically, the ones you want for a UIButton would be of the subtype UIControlEventTouch....
You should be crashing right away just cause that method doesn't exist.
That being said, without looking at your code we can't really tell you exactly which line inside your -myAction: method causes the crash. But the important point here is, the method whose signature / selector you are registering via the target-action pattern doesn't necessarily even need to have a parameter of type id, UIButton or anything.
Basically, when you do -addTarget:action:forControlEvents:you are telling a subclass of UIControl that when it undergoes the desired event/s, it should invoke a method in the object you pass to the first parameter of -addTarget: (the target), whose signature is the selector you pass to action:. The selector you pass to this parameter can have one or zero parameters in turn. If you pass in one with none (say, your action method is -doSomething), when the UIControl responds to the UIControlEvent it'll simply call your method and that's that. If instead, you pass in a selector that takes one parameter, the UIControl that triggered the action is automatically passed in to that parameter, cast to whatever type your action method's parameter type is.
So for instance:
if you register like so:
[self.readingListButton addTarget:self action:#selector(doSomething) forControlEvents:UIControlEventTouchUpInside];
your action method would look like this:
- (void)doSomething
{
// Notice we don't have a parameter and so we are limited to doing stuff
// that does not require the sender to be passed in.
NSLog(#"do Something!");
}
If instead you register like this (notice the : in the selector):
[self.readingListButton addTarget:self action:#selector(doSomething:) forControlEvents:UIControlEventTouchUpInside];
You could go:
- (void)doSomething:(id)sender
{
// If the button triggers the method, sender will be an id pointer holding the memory
// address of a UIButton and we could cast it to UIButton like so: UIButton *b = (UIButton)sender
// Then again, sender might not be a button. As long as we stick to stuff that any object
// will respond to we are fine though.
NSLog(#"do Something! %#", sender);
}
or:
- (void)doSomething:(UIButton*)sender
{
// We straight out assume it is a button:
NSLog(#"do Something! %#", sender);
}
So as you can see, the choice of parameter vs no parameter and the parameter type is sort of up to you.
Now my guess is the reason why your code crashes is because when you manually call the action method you are passing it an NSNumber instead of a UIButtonand inside the method you do something with the parameter that treats it as a UIBUtton.
Think about this for instance:
- (void)doSomething:(UIButton*)sender
{
// Our parameter is a button, so we can totally change its state:
sender.selected = !sender.selected;
}
If you pass in a button to the above method, it'll work just fine. However in your second case, you pass a number. and what really happens is this:
NSNumber *n;
UIButton *b = (UIButton*)n;
[target doSomething:b];
And inside -doSomething:
- (void)doSomething:(UIButton*)sender
{
// Our parameter is a button, so we can totally change its state:
sender.selected = !sender.selected;
}
But sender now is not really a button. It's a number cast to button. The minute the code above tries to change the state, it attempts to call the method -setState: on a NSNumber which does not have that method, and so you'd get a classic exception along the lines of:
unrecognized selector sent to instance.
So bottom line, if you don't require to pass in the button or any info into the target method, just define it without parameters or, if you do require a parameter, either make the parameter polymorphic (type id) and inside your method check to see what it is and act accordingly, or stick to a parameter of type UIButton but them make sure you only call it passing in buttons.

using tag attribute with a UIButton in the sender and trying to cast id -> UIButton

First time using tag attributes and wondering what I'm doing wrong here. I have two UIButtons that go to the same selector. I would like to add a tag to differentiate like this:
buttonOne.tag=1;
buttonTwo.tag=2;
In the selector that responds, I'm trying to get this tag out of the sender but being told that tag is not found on object of type '__strong id'. I know this way is pretty hacky but is there a simple way to get this to work?
-(void)buttonClicked:(id)sender
{
NSLog(#"you were clicked with %d", (UIButton *)sender.tag);
[sender setSelected:YES];
}
thx in advance
Yap:
-(void)buttonClicked:(UIButton *)sender
Voila.
Or, if you're concerned to use that ugly cast, at least pay attention to operator precedence:
((UIButton *)sender).tag

Resources