I have 3 buttons in my storyboard and ViewController that are working as expected:
- (IBAction)button0:(id)sender {
[sender setTitle:#"btn0 pressed" forState:UIControlStateNormal];
}
- (IBAction)button1:(id)sender {
[sender setTitle:#"btn1 pressed" forState:UIControlStateNormal];
}
- (IBAction)button2:(id)sender {
[sender setTitle:#"btn2 pressed" forState:UIControlStateNormal];
}
I have a fourth button that, when pressed, I would like to change the displayed text of button0-2 to an empty string.
- (IBAction)resetAllButtons:(id)sender {
//In Android this code would be something like:
//Button btn0 = (Button) findViewById(R.id.button0);
//btn0.setText(" ");
}
How do I do this? I've found many ways to change the button text, but only of the current button being pressed. Can I target all the buttons by id somehow?
Figured it out (although still not clear on why it works?)
I connected button0 as an IBOutlet in my ViewController.h file.
#property (weak, nonatomic) IBOutlet UIButton *button0;
From there I was able to reference it in my ViewController.m file using
[self.button0 setTitle:#" " forState:UIControlStateNormal];
But why am I able to do that? I thought that if I declared
- (IBAction)button0:(id)sender;
in my ViewControler.h file that I couldn't also have an outlet connected to the same object? Thanks for reading either way.
my code :
UIButton *boutonSuppr = [UIButton buttonWithType:UIButtonTypeRoundedRect];
boutonSuppr.frame = rectBoutonSuppr;
[boutonSuppr setBackgroundImage: [UIImage imageNamed:#"croixTest.png"] forState:UIControlStateNormal];
[monScrollView addSubview: boutonSuppr];
int numb = 10;
[boutonSuppr addTarget:self action:#selector(boutonSupprAppuye) forControlEvents:UIControlEventTouchUpInside];
My method declared : - (void)boutonSupprAppuye:(int) numero;
My problem is that I need to send a parameter in the method because I have several UIButton. For example, I want to send "(int)numb" but when I do that :
[boutonSuppr addTarget:self action:#selector(boutonSupprAppuye:numero) forControlEvents:UIControlEventTouchUpInside];
It doesn't work.
Thank you in advance.
If you're really just using an int to identify the button that's being hit you should just set unique tags for your buttons in Interface Builder or when you create them in code, set up corresponding enums and then in your hit method, handle like so. Tags are already available in every UIView and specifically designed for that purpose.
typedef NS_ENUM(NSInteger, ButtonTag) {
e_buttonTag_unknown = 0,
e_buttonTag_doThing1Button, /* 1 */
e_buttonTag_doThing2Button /* 2 */
};
...
// Add this to the button creation code you posted
boutonSuppr.tag = e_buttonTag_doThing1Button;
// and set the button's action to #selector(buttonTapped:).
...
- (IBAction) buttonTapped: (id) sender
{
UIButton *whichButton = (UIButton *) sender;
ButtonTag whichTag = whichButton.tag;
switch (whichTag)
{
case e_buttonTag_doThing1Button:
// "Do thing 1" button was pressed.
break;
case e_buttonTag_doThing2Button:
// "Do thing 2" button was pressed.
break;
default:
// Some other button was pressed.
break;
}
}
When you declare a #selector(), it can be considered the same as a function pointer. It does not take parameters at that time; it takes parameters when the selector is called.
This particular selector will be called by the UIButton class when the UIControlEventTouchUpInside event is received. This means that the execution will be out of reach of your source code and any parameters passed into it will be done so automatically by the system.
If you want to attach data to a UIButton, the simplest way to do this is with a #property.
#interface MyButton : UIButton
#property (assign) int numero;
#end
#implementation MyButton
#end
Then your construction becomes:
MyButton *boutonSuppr = [MyButton buttonWithType:UIButtonTypeRoundedRect];
boutonSuppr.numero = numero;
// etc.
Action methods should take an object pointer as argument.
Normally this is type id but sometimes a more specific object.
The key is it is a pointer and that means you can stuff anything in there in C if you pass in a pointer.
For ease just use an object and wrap your value in the object.
You could use NSValue for any C type wrapper or NSNumber for scalars
This is what I would use to change the text if it were the same method but I want to change it from a different method.
-(IBAction)StartTimer:(id)sender {
[sender setTitle:#"Stop" forState:UIControlStateNormal];
}
-(IBAction)ResetAllData:(id)sender {
[(NEED THIS PIECE) setTitle:#"Start" forState:UIControlStateNormal];
}
There are several ways of doing it. Perhaps the simplest one is to make an IBOutlet for the other button, give it a name (say, anotherButton), attach it in the storyboard, and call it directly, like this:
-(IBAction)ResetAllData:(id)sender {
[anotherButton setTitle:#"Start" forState:UIControlStateNormal];
}
An alternative would be to add a tag * to the button in question (say, 123), and then reference it by its numeric tag, like this:
-(IBAction)ResetAllData:(id)sender {
[[self viewWithTag:123] setTitle:#"Start" forState:UIControlStateNormal];
}
* To add a tag, select the button in the interface builder, open its properties, find the tag, and type in the desired value in the text field.
Make the UIButton you want to change the title of a property of the class, then you will have direct access to it. For example, the following could be put in your ViewController's .m file.
#interface ViewController ()
#property (nonatomic) UIButton *otherButton;
#end
Then in your first IBAction, just set the title of otherButton.
-(IBAction)ResetAllData:(id)sender {
[self.otherButton setTitle:#"Start" forState:UIControlStateNormal];
}
I have a main view with 3 buttons. Clicking on any of the buttons adds a SubView.
The buttons have different titles and are all linked to IBAction "switchView"
The "switchView" code is below.
- (IBAction)switchView:(id)sender{
secondView *myViewController = [[secondView alloc] initWithNibName:#"secondView" bundle:nil];
[self.view addSubview:myViewController.view];
}
The "secondView" loads up correctly and everything works well.
The problem is I want to be able to know which button was the Sender.
I don't want to create 3 subviews, one for each button. The code and XIB would be absolutely the same>
The only difference would be a variable that I would like to set up in the second view (viewDidLoad method) depending on who is the Sender (which button was clicked)
Is this possible? Or I would need to create 3 subViews - one for each button?
Your help is greatly appreciated!
You can identify different buttons with the tag property.
e.g. with your method:
-(IBAction)switchView:(id)sender {
UIButton *button = (UIButton*)sender;
if (button.tag == 1) {
//TODO: Code here...
} else if (button.tag == 2) {
//TODO: Code here...
} else {
//TODO: Code here...
}
}
The tag property can be set via the InterfaceBuilder.
Hope this helps.
I think you can solve in 2 ways:
Create a property like:
#property (nonatomic, strong) IBOutlet UIButton *button1, *button2, *button3;
in your viewcontroller and link the buttons to them as referencing outlet on the XIB.
Give a different tag to each button on your xib and ask for the tag of the sender with UIButton *b=(UIButton*)sender; b.tag; like Markus posted in detail.
Solving my problem it all came down to transferring data between the mainView and subView.
In my mainView.h I declared an NSString and its #property
...
NSString *btnPressed;
}
#property(nonatomic, retain) NSString *btnPressed;
...
then in my mainView.m inside the switchView method I did this:
- (IBAction)switchView:(id)sender{
secondView *myViewController = [[secondView alloc] initWithNibName:#"secondView" bundle:nil];
btnPressed = [NSString stringWithFormat:#"%i", [sender tag]];
[myViewController setBtnPressed:self.btnPressed];
[self.view addSubview:myViewController.view];
}
This line in the code above actually takes care of transferring the data to the newly created subView:
[myViewController setBtnPressed:self.btnPressed];
Then in my secondView.h I declare exactly the same NSString *btnPressed and its #property (though this a completely different object than the one declared in main)
Then in my secondView.m I get the value of the button pressed I'm interested in.
- (void)viewDidLoad {
[super viewDidLoad];
int theValueOfTheButtonPressed = [self.btnPressed intValue];
}
This works well.
Don't forget to #synthesize btnPressed; as well as [btnPressed release]; in both mainView.m and secondView.m
I am using addTarget:action:forControlEvents like this:
[newsButton addTarget:self
action:#selector(switchToNewsDetails)
forControlEvents:UIControlEventTouchUpInside];
and I would like to pass parameters to my selector "switchToNewsDetails".
The only thing I succeed in doing is to pass the (id)sender by writing:
action:#selector(switchToNewsDetails:)
But I am trying to pass variables like integer values. Writing it this way doesn't work :
int i = 0;
[newsButton addTarget:self
action:#selector(switchToNewsDetails:i)
forControlEvents:UIControlEventTouchUpInside];
Writing it this way does not work either:
int i = 0;
[newsButton addTarget:self
action:#selector(switchToNewsDetails:i:)
forControlEvents:UIControlEventTouchUpInside];
Any help would be appreciated :)
action:#selector(switchToNewsDetails:)
You do not pass parameters to switchToNewsDetails: method here. You just create a selector to make button able to call it when certain action occurs (touch up in your case). Controls can use 3 types of selectors to respond to actions, all of them have predefined meaning of their parameters:
with no parameters
action:#selector(switchToNewsDetails)
with 1 parameter indicating the control that sends the message
action:#selector(switchToNewsDetails:)
With 2 parameters indicating the control that sends the message and the event that triggered the message:
action:#selector(switchToNewsDetails:event:)
It is not clear what exactly you try to do, but considering you want to assign a specific details index to each button you can do the following:
set a tag property to each button equal to required index
in switchToNewsDetails: method you can obtain that index and open appropriate deatails:
- (void)switchToNewsDetails:(UIButton*)sender{
[self openDetails:sender.tag];
// Or place opening logic right here
}
To pass custom params along with the button click you just need to SUBCLASS UIButton.
(ASR is on, so there's no releases in the code.)
This is myButton.h
#import <UIKit/UIKit.h>
#interface myButton : UIButton {
id userData;
}
#property (nonatomic, readwrite, retain) id userData;
#end
This is myButton.m
#import "myButton.h"
#implementation myButton
#synthesize userData;
#end
Usage:
myButton *bt = [myButton buttonWithType:UIButtonTypeCustom];
[bt setFrame:CGRectMake(0,0, 100, 100)];
[bt setExclusiveTouch:NO];
[bt setUserData:**(insert user data here)**];
[bt addTarget:self action:#selector(touchUpHandler:) forControlEvents:UIControlEventTouchUpInside];
[view addSubview:bt];
Recieving function:
- (void) touchUpHandler:(myButton *)sender {
id userData = sender.userData;
}
If you need me to be more specific on any part of the above code — feel free to ask about it in comments.
Need more than just an (int) via .tag? Use KVC!
You can pass any data you want through the button object itself (by accessing CALayers keyValue dict).
Set your target like this (with the ":")
[myButton addTarget:self action:#selector(buttonTap:) forControlEvents:UIControlEventTouchUpInside];
Add your data(s) to the button itself (well the .layer of the button that is) like this:
NSString *dataIWantToPass = #"this is my data";//can be anything, doesn't have to be NSString
[myButton.layer setValue:dataIWantToPass forKey:#"anyKey"];//you can set as many of these as you'd like too!
*Note: The key shouldn't be a default key of a CALayer property, consider adding a unique prefix to all of your keys to avoid any issues arising from key collision.
Then when the button is tapped you can check it like this:
-(void)buttonTap:(UIButton*)sender{
NSString *dataThatWasPassed = (NSString *)[sender.layer valueForKey:#"anyKey"];
NSLog(#"My passed-thru data was: %#", dataThatWasPassed);
}
Target-Action allows three different forms of action selector:
- (void)action
- (void)action:(id)sender
- (void)action:(id)sender forEvent:(UIEvent *)event
I made a solution based in part by the information above. I just set the titlelabel.text to the string I want to pass, and set the titlelabel.hidden = YES
Like this :
UIButton *imageclick = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
imageclick.frame = photoframe;
imageclick.titleLabel.text = [NSString stringWithFormat:#"%#.%#", ti.mediaImage, ti.mediaExtension];
imageclick.titleLabel.hidden = YES;
This way, there is no need for a inheritance or category and there is no memory leak
I was creating several buttons for each phone number in an array so each button needed a different phone number to call. I used the setTag function as I was creating several buttons within a for loop:
for (NSInteger i = 0; i < _phoneNumbers.count; i++) {
UIButton *phoneButton = [[UIButton alloc] initWithFrame:someFrame];
[phoneButton setTitle:_phoneNumbers[i] forState:UIControlStateNormal];
[phoneButton setTag:i];
[phoneButton addTarget:self
action:#selector(call:)
forControlEvents:UIControlEventTouchUpInside];
}
Then in my call: method I used the same for loop and an if statement to pick the correct phone number:
- (void)call:(UIButton *)sender
{
for (NSInteger i = 0; i < _phoneNumbers.count; i++) {
if (sender.tag == i) {
NSString *callString = [NSString stringWithFormat:#"telprompt://%#", _phoneNumbers[i]];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:callString]];
}
}
}
As there are many ways mentioned here for the solution, Except category feature .
Use the category feature to extend defined(built-in) element into your
customisable element.
For instance(ex) :
#interface UIButton (myData)
#property (strong, nonatomic) id btnData;
#end
in the your view Controller.m
#import "UIButton+myAppLists.h"
UIButton *myButton = // btn intialisation....
[myButton set btnData:#"my own Data"];
[myButton addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
Event handler:
-(void)buttonClicked : (UIButton*)sender{
NSLog(#"my Data %#", sender. btnData);
}
You can replace target-action with a closure (block in Objective-C) by adding a helper closure wrapper (ClosureSleeve) and adding it as an associated object to the control so it gets retained. That way you can pass any parameters.
Swift 3
class ClosureSleeve {
let closure: () -> ()
init(attachTo: AnyObject, closure: #escaping () -> ()) {
self.closure = closure
objc_setAssociatedObject(attachTo, "[\(arc4random())]", self, .OBJC_ASSOCIATION_RETAIN)
}
#objc func invoke() {
closure()
}
}
extension UIControl {
func addAction(for controlEvents: UIControlEvents, action: #escaping () -> ()) {
let sleeve = ClosureSleeve(attachTo: self, closure: action)
addTarget(sleeve, action: #selector(ClosureSleeve.invoke), for: controlEvents)
}
}
Usage:
button.addAction(for: .touchUpInside) {
self.switchToNewsDetails(parameter: i)
}
There is another one way, in which you can get indexPath of the cell where your button was pressed:
using usual action selector like:
UIButton *btn = ....;
[btn addTarget:self action:#selector(yourFunction:) forControlEvents:UIControlEventTouchUpInside];
and then in in yourFunction:
- (void) yourFunction:(id)sender {
UIButton *button = sender;
CGPoint center = button.center;
CGPoint rootViewPoint = [button.superview convertPoint:center toView:self.tableView];
NSIndexPath *indexPath = [self.tableView indexPathForRowAtPoint:rootViewPoint];
//the rest of your code goes here
..
}
since you get an indexPath it becames much simplier.
See my comment above, and I believe you have to use NSInvocation when there is more than one parameter
more information on NSInvocation here
http://cocoawithlove.com/2008/03/construct-nsinvocation-for-any-message.html
This fixed my problem but it crashed unless I changed
action:#selector(switchToNewsDetails:event:)
to
action:#selector(switchToNewsDetails: forEvent:)
I subclassed UIButton in CustomButton and I add a property where I store my data. So I call method: (CustomButton*) sender and in the method I only read my data sender.myproperty.
Example CustomButton:
#interface CustomButton : UIButton
#property(nonatomic, retain) NSString *textShare;
#end
Method action:
+ (void) share: (CustomButton*) sender
{
NSString *text = sender.textShare;
//your work…
}
Assign action
CustomButton *btn = [[CustomButton alloc] initWithFrame: CGRectMake(margin, margin, 60, 60)];
// other setup…
btnWa.textShare = #"my text";
[btn addTarget: self action: #selector(shareWhatsapp:) forControlEvents: UIControlEventTouchUpInside];
If you just want to change the text for the leftBarButtonItem shown by the navigation controller together with the new view, you may change the title of the current view just before calling pushViewController to the wanted text and restore it in the viewHasDisappered callback for future showings of the current view.
This approach keeps the functionality (popViewController) and the appearance of the shown arrow intact.
It works for us at least with iOS 12, built with Xcode 10.1 ...