My iPhone app has many buttons and I want all the buttons to call the same method, but with different parameters.
For example I want tapping one button to call the method myMethod: with the argument #"foo", and a second button should call the same method but with argument #"bar".
The so called "IBActions" must have one of these signatures:
-(void)action;
-(void)actionWithSender:(id)sender;
-(void)actionWithSender:(id)sender event:(UIEvent*)event;
You cannot add any other parameters. Nevertheless you can use sender (which is button1 or button2 in your case) to get the parameter:
-(void)actionWithSender:(UIButton*)sender {
NSString* parameter;
if (sender.tag == 1) // button1
parameter = #"foo";
else // button2
parameter = #"bar";
...
}
the real reason You cannot add additional parameter is that UIKIT will push params on the stack.
so the only way is to use tags.
A DIRTY way can be to convert a pointer to int and tagging the button with it:
myStruct params;
// fill params:
params.x=....
params.y=....
params.z=....
UIButton * btn = [UIButton......]; // create or use one from XIB
btn.tag = (int)¶ms;
... in Call back:
-(IBActions) doIt:(id)sender
{
myStruct * paramsPtr = (myStruct*)tag;
int i = paramsPtr->x;
NOTE: params MUST be keep static .. or allocate using malloc (more and more ugly code...).
DO NOT use a local var: it will be allocated on stack so will be removed after exiting from the setup method.
Give your various UIButton instances different tag property values.
In your IBAction method -myMethod:, you might then do something like:
- (void) myMethod:(id)sender {
switch (sender.tag) {
case (firstButtonTag):
doFooStuff;
break;
case (secondButtonTag):
doBarStuff;
break;
// etc.
}
}
The values firstButtonTag and secondButtonTag can be stored in an enum if you want to make this easy to maintain.
You can't pass parameters through an IBAction. What I usually do is give the buttons the unique tag in IB. THe tag is an integer value so I u then use a simple lookup table to convert the tag to some value.
In this case, three buttons but tags 1 to 3:
- (IBAction) buttonPressed: (UIButton*) sender
{
static const NSString* names = { #"Foo", #"Bar", #"Baz" };
id tag = [sender tag];
if (tag >= 1 && tag <= 3) {
NSLog(#"Button pressed is %#", names[tag]);
}
}
(id)Sender is shows that whatever u pass on UIButton click event is directly pass to this method and no matter that what type it is , it take automatically like if you pass button tag then it take button tag as sender.tag etc
As others have mentioned you cannot pass your custom parameter into action method. If you do not like the solution using tags you may also subclass UIButton with your custom class and add your parameter there. (By I wouldn't bother and just use tags)
You don't. The only parameter is the sender object, which you may use to have a different behavior, but what I'd do is define 2 action methods, which simply in turn call the same method with a different parameter, i.e. you'd have:
- (IBAction)button1:(id)sender
{
[self doStuff:kButton1];
}
- (IBAction)button2:(id)sender
{
[self doStuff:kButton2];
}
- (void)doStuff:(ParamType)param;
{
...
}
In defense of that method (no pun intended), I'd add that it makes clearer when you review your UI with Interface Builder to see that different buttons actually have different effects, which is harder to tell if they all call whateverAction:
Related
I'm trying to add keyboard shortcuts to my app and I'm having a problem with the action of the UIKeyCommand not being called.
I got a UIViewController that that is overriding the KeyCommands.
- (BOOL)becomeFirstResponder
{
return YES;
}
- (NSArray<UIKeyCommand *> *)keyCommands
{
return self.keyCommandManager.keyShortcutsArray;
}
I also have a KeyCommandManager class of NSObject which has two methods one that sets the keyShortcutsArray depending on the state of my app and the other one is the method that should be tigger by the UIKeyCommands.
- (void)setKeyShortcutsOfType:(ShortcutType)shortcutType
{
switch(shortcutType)
{
case PlaybackPreviewShortcut:
self.keyShortcutsArray = #[[UIKeyCommand keyCommandWithInput:#" " modifierFlags:0 action:#selector(keyShortcutActions:) discoverabilityTitle:#"Toggle playback preview"]];
break;
default:
self.keyShortcutsArray = #[];
break;
}
- (void)keyShortcutActions:(UIKeyCommand)sender
{
NSLog(#"#### This method is not being called by the space key shortcut");
}
Currently when a key is pressed the KeyCommand override method is getting the correct array. However the selector of those keys are not working and the keyShortcutActions method is not being called.
From Apple's docs:
The key commands you return from this method are applied to the entire responder chain. When an key combination is pressed that matches a key command object, UIKit walks the responder chain looking for an object that implements the corresponding action method. It calls that method on the first object it finds and then stops processing the event.
Your keyCommandManger instance of NSObject is not in the responder chain -- the view controller is.
If you put this method:
- (void)keyShortcutActions:(UIKeyCommand)sender
{
NSLog(#"#### This method IS being called (in view controller) by the space key shortcut");
}
you should see it being triggered.
If you want your "action" code to be contained in keyCommandManger, you could forward the event to your manager object. Or, you could try to change your manager class to inherit from UIResponder -- but reliably getting it into the chain is the tough part.
I created a single view app, added a label, an un-editable text view and a button, I have an array of strings. Really simply just want to click the button and change the string at random.
- (IBAction)viewNextPressed:(id)sender {
NSArray *affirmationStrings = #[
#"String 1 Pressed",
#"String 2 Pressed",
#"String 3 Pressed"
];
//Generate a random index from our array
int randomNIndex = arc4random() % [affirmationStrings count];
//Display a string from our array
self.displayAffirmationText.text = affirmationStrings[randomNIndex];
}
#end
Obviously this works fine for this example but its horribly inefficient as its generating the array each time the button is clicked. Where is the best place to store the array so its generated upon load and I can just access it when needed?
I see viewDidLoad but as a beginner I want to try to understand best practice for simple tasks. Secondly is the way I am storing strings fine for a large sample of say 500-1k+ strings?
For a relatively small number of strings, a better option would be to:
add either a property or an instance variable to your class
initialise said property or instance variable either in your init method, or, in the case of a View Controller, in viewDidLoad.
So, in the case of an instance variable:
#implementation MyViewController
{
NSArray *_affirmationStrings;
}
...
- (void)viewDidLoad
{
[super viewDidLoad];
_affirmationStrings = #[ ... list of strings ... ];
}
Then refer to it via _affirmationStrings.
In the case of a property, visible to other classes, read-only, with lazy initialization:
In .h:
#interface MyViewController : UIViewController
#property (readonly) NSArray *affirmationStrings
#end
In .m:
- (NSArray *)affirmationStrings
{
if (!_affirmationStrings)
_affirmationStrings = #[ ... list of strings ... ]
return _affirmationStrings;
}
Then refer to it via self.affirmationStrings.
There are also alternatives to make it read/write (so you can set the values from another class), or visible only within the class, etc.
If you want to handle lots of strings, you probably at the very least want to move the list outside of your code, to an external file (text file, JSON, XML, plist...). You can then either load it from there at once and keep it, or load it on demand (and forget about it once you no longer need it, hence reloading it again if you need it again).
You could also store the data in a database, either via Core Data or directly with SQLite.
It really all depends on your goals/requirements.
I am trying to work out the correct way to approach some methodology.
Workflow
When a game is created, I would like to first search to see if a game already exists with this user. If there is a game I will not create one and show a message to the user.
At present I have two methods:
+(void)createNewGameAgainst:(PFUser *)user2 withCompletion:(void (^)(BOOL success))completionHandler
+(BOOL)checkIfGameAlreadyExistsAgainst:(PFUser *)opponentUser
The createNewGame... method is called first. Then within this I make a call to [self checkIfGameAlreadyExistsAgainst:user2];.
How do I check the result of the second method, from within the first? So how do I determine what the BOOL value is of the call to the method checkIfGameAlreadyExistsAgainst?
Is this the correct way to approach this or is there a better/cleaner way possibly?
The return value of a function can be used like a variable:
BOOL gameExists = [self checkIfGameAlreadyExistsAgainst:user2]; // assign result to a new variable
if(gameExists == YES) // compare result to YES
{
}
You can skip creating a new variable and just compare the result
if ([self checkIfGameAlreadyExistsAgainst:user2] == YES) // compare result directly
{
}
And when the type is BOOL, you can omit the comparison and just do this:
if ([self checkIfGameAlreadyExistsAgainst:user2])
{
}
I am learning Objective-C and iOS development. I think I have a handle on method syntax. Method Syntax in Objective C
If I am understanding correctly, this instance method called reset should return an IBAction object. But it just seems to be setting instance variables (which are UI text fields with those names). Why doesn't it have a return statement returning an IBAction object?
- (IBAction)reset { //why does it not have a return statement?
fahrenheit.text = #"32";
celsius.text = #"0";
kelvin.text = #"-273.15";
}
I am used to .NET code that would look like this (putting it in pseudocode for non-NET folks):
public function reset () returns IBAction
me.fahrenheit.text = "32";
me.celsius.text = "0"
me.kelvin.text = "-273.15"
return new IBAction //I would expect something like this in obj C, based on my experience with .NET languages
end function
IBAction is typedef'd void. It is used by Xcode as a hint that the method is to be available for "hookup" in interface builder.
I have the following IBAction that is linked to several switch in my application. I would like to figure out which switch is clicked. Each UISwitch has a specific name. I want that name.
- (IBAction)valueChanged:(UISwitch *)theSwitch { //Get name of switch and do something... }
You can either use tags:
When you create the switches you need to set their tags.
- (IBAction)valueChanged:(UISwitch *)theSwitch {
switch(theSwitch.tag){
case 0:
{
//things to be done when the switch with tag 0 changes value
}
break;
case 1:
{
//things to be done when the switch with tag 0 changes value
}
break;
// ...
default:
break;
}
}
Or check if the switch is one of your controller properties
- (IBAction)valueChanged:(UISwitch *)theSwitch {
if(theSwitch == self.switch1){
//things to be done when the switch1 changes value
} else if (theSwitch == self.switch2) {
//things to be done when the switch2 changes value
}// test all the cases you have
}
The IBAction passes a pointer to the switch that performed the action. You can get any property off of it.
To compare switches:
- (void)valueChanged:(UISwitch *)theSwitch {
if ([theSwitch isEqual:self.switch1]) {
NSLog(#"The first switch was toggled!");
}
else if ([theSwitch isEqual:self.switch2]) {
NSLog(#"The second switch was toggled!");
}
else {
NSLog(#"Some other switch was toggled!");
}
}
I don't thank you can get the name of that switch. You could tag each of the switches, and use that tag to determine the name of the switch.
UISwitch doesn't have a name property. But you can subclass it and add a name property to the subclass. Then create switches from the subclass instead of UISwitch and give them a name when you init them.
#class MySwitch : UISwitch
#property (nonatomic, retain) NSString* name;
#end
Then the event handler can access them name string:
- (IBAction)valueChanged:(MySwitch *)theSwitch {
NSLog(#"switch %# value changed", theSwitch.name);
}
But I think the better answer is to use the tag field already there and use integer tags to identify the switches rather than a string. You can create enumeration constants in your code to name the tag values:
enum { SomeSwitch = 1, AnotherSwitch = 2, MainSwitch = 3 } _SwitchTags;
The best answer is the one #Moxy mentioned to compare the switch's pointer to your controller's properties to figure out which switch changed. That's what I do in my code. Tags and names are too error prone in the long run.