I'm new to Obj-C and I have run in to a very, very basic mental block that's making me feel a little stupid. I want to pass a variable from one method to another in an purely objective-C way. So far I'm stuck and have resorted to writing it in C. In C it goes as follows;
//detect the change in the segmented switch so we can; change text
- (IBAction)switchChanged:(id)sender
{
NSLog(#"Switch change detected");
//grab the info from the sender
UISegmentedControl *selector = sender;
//grab the selected segment info from selector. This returns a 0 or a 1.
int selectorIndex = [selector selectedSegmentIndex];
changeText (selectorIndex);
}
int changeText (int selectorPosition)
{
NSLog(#"changeText received a value. Selector position is %d", selectorPosition);
//Idea is to receive a value from the segmented controler and change the screen text accordingly
return 0;
}
This works perfectly well, but I want to learn how to do this with objects and messages. How would I rewrite these two methods to do this?
Cheers
Rich
Actually, you will only need to rewrite one of them since - (IBAction)switchChanged:(id)sender is an objective c method.
once you have your class definition, you can rewrite the changeTextFunction as:
//detect the change in the segmented switch so we can; change text
- (IBAction)switchChanged:(id)sender
{
NSLog(#"Switch change detected");
//grab the info from the sender
UISegmentedControl *selector = sender;
//grab the selected segment info from selector. This returns a 0 or a 1.
int selectorIndex = [selector selectedSegmentIndex];
[self changeText:selectorIndex];
}
-(int) changeText:(int) selectorPosition
{
NSLog(#"changeText received a value. Selector position is %d", selectorPosition);
//Idea is to receive a value from the segmented controler and change the screen text
return 0;
}
Also note that you will should add to your header file:
-(int) changeText:(int) selectorPosition;
Also note that this is for adding the changeText method to the class that has the switchChanged method. Tip: use command+option+up to jump to the header file directly.
Related
I have an IBAction called keyboardResponse associated with a text field called myTextFieldIBOutlet via the "Editting Changed" event handler in the xib:
- (IBAction)keyboardResponse:(id)sender
{
// process this single character - function I wrote else where that works fine.
[self processSingleCharacter:myTextFieldIBOutlet.text];
// clear input text
myTextFieldIBOutlet.text = #"";
}
It's supposed to clear the input after the user types something into it.
I get a run time error with this code in iOS Simulator:
Thread 1: EXC_BAD_ACCESS (code=2, address=0xbf7fff0c)
Why? I had synthesized the IBOutlet myTextFieldIBOutlet already.
if myTextFieldIBOutlet is synthesized, you should change the last line to:
self.myTextFieldIBOutlet.text = #"";
If the textfield you want to clear is the same control that calls this action, you can also use the sender variable you are sending
[sender setText:#""];
I have problem that im trying to get solve for like week.
My goal is to get variable out of my IBAction, to use for example in -(void)viewDidLoad..
But as far as I am now I can use my variable only in my IBAction..
- (IBAction) changeLat:(NSNumber *)str {
longi = str;
double lop = longi.doubleValue;
NSLog(#"%f",lop);
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog (#"%#",lop);
}
It NSLog shows everything fine in action, but in view did load it doesn't even recorganize it.
If you create a variable inside of -IBAction, the scope of that variable is only that method, so you cannot access to that variable outside it.
If you want your variable to be global to your class, you have to create it in the declaration of your class, like this:
#interface MainViewController () {
#private
double lop;
}
Put this at the beginning of your .m file, and then lop would be accesible in all your class.
You can read more about the scope of the variables here:
http://www.techotopia.com/index.php/Objective-C_Variable_Scope_and_Storage_Class
Actually, IBAction is converted to void by the preprocessor. It's used by Interface Builder as a label that identifies this method as an action able to be related from an IB Object.
There's no way (AFAIK) to use two return types in a function (for example `(IBAction double)´, equivalent to ´(void double)´), but a good practice could be something like this:
- (IBAction)changeLatAction:(id)sender {
NSNumber *str = <get the NSNumber from a valid place>;
[self changeLat:str];
}
- (double) changeLat:(NSNumber *)str {
longi = str;
double lop = longi.doubleValue;
NSLog(#"%f",lop);
return ????;
}
Your first declaration of changeLat seems to be wrong, because as a first parameter you'll always get the "sender" or "caller" object, related from IB (when called from an action, of course), so, you need to get the str value from a valid place.
Cheers.
There are 24 buttons that their labels change from a function driven by a UISegmentedControl that has 7 segments. There is also a UIPickerView with 9 different objects. These two dynamically change the labels of the buttons. Then, when a button is pressed i have to know the UIPickerView object that is selected, the UISegmentedControl selection and which button is this.
My function that receives and executes is this:
- (int)ButtonPlayController:(int)buttonPressed {
NSString *button = [NSString stringWithFormat:#"%#%i", #"button", buttonPressed];
if (button.titleLabel.text == #"1C") {
[self Play1C];
}
else if .....
}
The function receives a number (the button number) and combines the string name to create the UIButton name, e.g. if the function receives number 8 then the UIButton name is button8 (i already have on my .h file: IBOutlet UIButton *button8;)
I remember these work on PHP but what about Object C? What do i have to change for this to work?
Looks like there some pointers for you in the comments, but I'm wondering why you are implementing your target-action this way? Maybe there is a part of your program I don't understand but why not just pass the button as the argument to the receiver of the action:
- (IBAction)doStuffWithButton:(id)sender
{
if ( sender.tag == 1 )
{
[self actOnOne];
}
else if ( sender.tag == 2 )
{
....
}
}
Like I say, this doesn't answer your original question, but maybe a different design pattern would make things easier?
So I am trying pass in parameters to my "buttonClicked" function so that I can dynamically control what happens when you click the button. Every way that I try to do it on my own just breaks my code. I have searched many different sites and answers on stackOverflow, but I can't seem to find an answer. I am fairly new to objective C, especially with functions, So I could really use some help figuring out how to do this. Thanks in advance
Here is my code thus far and what I am trying to do:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
NSLog(#"Hi 1!");
[button addTarget:self action:#selector(buttonClicked:buttonType:buttonID:) forControlEvents:UIControlEventTouchUpInside];
button.frame = CGRectMake(buttonViewXvalue, buttonViewYvalue, buttonViewWidth, buttonViewLength);
[self.view addSubview:button];
Then the Declaration in the Header File:
- (IBAction)buttonClicked:(id)sender theButtonType: (int)buttonType: theButtonID: (int) buttonID;
and the implementation:
- (IBAction)buttonClicked:(id)sender theButtonType: (int)buttonType: theButtonID: (int) buttonID
{
//Here I would use the buttonType and buttonID to create a new view.
NSLog(#"Hi!");
}
You can't use multi-parameter methods with addTarget:action:forControlEvents:. Instead you might set the button's tag, then look up information later based on the tag.
The action you add to UIButton (or any UIControl for that matter) must have a signature like (void)actionName or (void)actionName:(id)sender; as defined by target-action design pattern.
That gives you two simple solutions. One is that each of your buttons calls different (void)actionName-like method, which then calls a more complex method on self and passes the required parameters. Other way is to give each of your buttons a tag property and have them call (void)actionName:(id)sender-like method (they can all call the same one) and then you call the right method with right parameters depending on this tag:
- (void)actionName:(UIButton)sender;
{
if (sender.tag == 1) {
[self firstMethodWithString:someString andNumber:someNumber];
} else if (sender.tag == 2) {
[self secondMethodWithArray:someArray dictionary:someDictionary andColor:someColor];
} // and so on
}
Notice how I changed sender from id to UIButton here. This enables you to call tag without casting and not get a compiler warning, because compiler know you only expect UIButton instances to call this method.
If you really wanted to you could create a separate callback for each button, like:
// In your Whatever.h file
- (IBOutlet)actionButton1:(UIButton *)sender;
- (IBOutlet)actionButton2:(UIButton *)sender;
// In your Whatever.m file
- (IBOutlet)actionButton1:(UIButton *)sender
{
// do button 1 specific stuff
}
- (IBOutlet)actionButton2:(UIButton *)sender
{
// do button 2 specific stuff
}
// etc you get the idea
Then from Interface Builder look at your Whatever.xib file. You can link the "Sent Event" (drag from right side column) of "Touch Up Inside" to any of those actions above which will pop up in "File's Owner" (left column, when you release drag). You can do a different one for each button.
I should mention that generally if these are variants of the same functionality it makes more sense to use the tag property of UIButton.
I have solved this problem with an array of objects: all the parameters are stored in one object, then the object is inserted in the array, finally, the index of the object in the array is passed in the TAG property of the button. This technique works for one or many buttons. I did it for an app that had a list of contacts, where you were able to accept or reject them with buttons, and this buttons were calling the same action method, thanks to the TAG it was possible to know what parameters send to database. Steps:
1- Create a new class : New File, Objective-C class, name it, subclass of NSOBJECT, save it.
2- In the header of this new class declare one property for each parameter.
3- Now go to the IMPLEMENTATION file of the class of the viewcontroller where your button belongs to.
4- Import your new class :
#import "new_class.h"
5- Declare the array in the INTERFACE section :
#interface my_viewcontroller ()
{ NSMutableArray * my_array; }
6- In the VIEWDIDLOAD method create the array as empty :
my_array = [ [ NSMutableArray alloc ] initWithObjects : nil ];
7- In the method where you get the data for the parameters, declare an object of the new class, instantiate it and fill the properties :
new_class * nc;
nc = [ [ new_class alloc ] init ];
nc.param1 = #"abc";
nc.param2 = 123;
nc.param3 = true;
8- Now insert it in the array :
[ my_array addObject : nc ];
9- Store the value 0 in the TAG of the button. You will use this value as index to access the parameters in the my_array[ 0 ] position.
If there are more buttons, for example, from a web service that returns JSON data, just loop through the data creating more instances of the new class and inserting them in the array. Later, for example, in a tableview with dynamic cells and a template cell with buttons, in the method CELLFORROWATINDEXPATH, you will be able to store the INDEXPATH value in the TAG of every button, so these buttons will access their own parameters.
I have a UILabel that adds one to its value -- basically it counts up -- when a button is pressed. Could someone please help me with a minus button? That way if the user accidentally presses the add button more than they needed, they can subtract their mistake. I've tried this, but the label's text is set to -1 now. and I want it to just subtract one each time its pressed:
- (IBAction)subtractButton:(id)sender {
static int integerSaved;
integerSaved = integer;
integerSaved -= 1;
[label2 setText:[NSString stringWithFormat:#"%i", integerSaved]];
}
Try this:
- (IBAction)subtractButton:(id)sender {
[label2 setText:[NSString stringWithFormat:#"%d",[label2.text intValue] - 1]];
}
-(void)subtract
{
label2.text = [NSString stringWithFormat:#"%d", [label2.text intValue]-1];
}
This code assumes that you are not using Interface Builder, and that you are manually linking "subtract" to the UIButton. If you are using Interface Builder, try this code.
-(IBAction)subtract:(id)sender
{
label2.text = [NSString stringWithFormat:#"%d", [label2.text intValue]-1];
}
I have not tested this but I think it should work. Good luck!
Using the value of the UIView instance to do arithmetic is not the MVC-way. You should really be separating your data model from your view.
Expose an int, NSInteger or NSNumber property in a class somewhere, where that class is dedicated to holding data values for your app.
When a touch event comes in for a given button, increment the data property and fire a notification that updates the view based on what's in the property. Or add an observer to the property. The observer then updates the view when the property value changes.
Following the MVC pattern would be a more "Apple"-ish or "iOS"-ish way of doing things, than getting the UIView's value, converting it to an integer, and then converting it back to a string.