This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
Uibutton events
-(void)myButtonclick {
NSString *data=[[NSString alloc]initWithString:#"YES U PRESSED BUTTON"];
UIButton *refreshbutton=[UIButton buttonWithType:UIButtonTypeCustom];
[refreshbutton setFrame:CGRectMake(15.0f, 330.0f, 150.0f, 32.0f)];
[refreshbutton setCenter:CGPointMake(80.0f,340)];
[refreshbutton setBackgroundImage: normalImage forState: UIControlStateNormal];
[refreshbutton setBackgroundImage: downImage forState: UIControlStateHighlighted];
[refreshbutton setBackgroundImage: selectedImage forState: UIControlStateSelected];
[refreshbutton setTitle:#"Refresh" forState:UIControlStateNormal];
[refreshbutton addTarget:self action:#selector(showMessage:) forControlEvents:UIControlEventTouchUpInside];
}
-(id)showMessage:(id)sender{
// Here I want to get the value of "data" which is defined the method
// "myButtonclick" in the first line. how it is possible..?
}
In the above code in the method "myButtonclick" I set one NSString variable name is "data" I want to get (print there) its value (YES U PRESSED BUTTON) in the method "showMessage" when I press that button.
I know this is done using #selector variable.. but I don't know how it is done
String objects and data objects are two different things. Don't name a string variable “data”—you're setting yourself and anyone else who reads the code up for confusion, and your program up for bugs.
#selector is not a variable. It's part of a literal expression.
Either define your variable that holds the string in showMessage: instead of myButtonClick, or make it an instance variable and create the string and assign it in init.
Making it an instance variable will also fix the leak you have (you alloc the string but never release it), as long as you release the string in dealloc. See the Memory Management entry in Cocoa Core Competencies for more detail on why your current code is wrong.
I'm also confused as to what myButtonClick is supposed to do. It certainly doesn't click the button—all it does is create it (which you'll find much easier to do in IB). Plus, you don't even put the button into a view; you create it and set it up, and then the method ends.
Related
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Objective C: Sending arguments to a method called by a UIButton
i have a problem with uibutton action. I want send argument when the button clicked. i saw some examples that work with tag prop. and the class is id sender. but i dont want to fixed it. the action goes to my own manager. how can i do ?
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:self
action:#selector(goToSubViewManager:)
forControlEvents:UIControlEventTouchUpInside];
here is the gotosubviewmng
- (void)goToSubViewManager:(ViewType*)vTipi
{
}
you should only use - (void)goToSubViewManager:(id)sender. or - (void)goToSubViewManager
so here are 2 ways to solve your problem.
you can use variables or property.
- (void)yourMethod
{
//your code
_vTipi = yourVTipi;
}
- (void)goToSubViewManager
{
//here you can use _vTipi;
}
you can add a Category for the UIButton , to add an id type property(such as #property(nonatomic) vTipi).faking instance variables for UIButton.(how to do you can look at this : http://oleb.net/blog/2011/05/faking-ivars-in-objc-categories-with-associative-references/) Then you can use like this:
- (void)goToSubViewManager:(id)sender
{
//you can user ((UIButton *)sender).vTipi
}
Use can use tag property of the button. And then can use that object by comparing the tag assigned.
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
[rightButton addTarget:self
action:#selector(goToSubViewManager:)
forControlEvents:UIControlEventTouchUpInside];
rightButton.tag=1;
//
then in the call back you can compare it as
- (void)goToSubViewManager:(id)sender
{
if(sender.tag==1){
// you can use an array of that object(if multiple), then apply your logic
}
}
I'm updating some old code, and to make more room in a toolbar, I'm converting the Buttons from test to images. An example of the new and old code in loadView is this:
// New code, doesn't work.
UIButton *toggleKeyboardBtn = [UIButton buttonWithType:UIButtonTypeCustom];
toggleKeyboardBtn.bounds = CGRectMake( 0, 0, showKeyboardImage.size.width, showKeyboardImage.size.height );
[toggleKeyboardBtn setImage:showKeyboardImage forState:UIControlStateNormal];
UIBarButtonItem *toggleKeyboardItem = [[UIBarButtonItem alloc] initWithCustomView:toggleKeyboardBtn];
[toggleKeyboardItem setTarget:self];
[toggleKeyboardItem setAction:#selector(toggleKeyboard:)];
// Original code, works jut fine.
UIBarButtonItem *setupItem = [[[UIBarButtonItem alloc] initWithTitle:#"Setup" style:UIBarButtonItemStyleBordered target:[UIApplication sharedApplication].delegate action:#selector(showSetupView:)] autorelease];
My new code is copied from Cannot set action on UIBarButtonItem, and I'm fairly certain that I'm not making their mistake since my text button is working just fine.
showSetupView() is in my AppController.m file, and the setup screen appears and disappears as the button is pressed.
toggleKeyboard(), OTOH, is in the same file as the loadView() routine, and currently consists of this code:
//- (void)toggleKeyboard {
- (IBAction)toggleKeyboard:(id)sender {
NSLog(#"Entering toggleKeyboard()...");
hiddenKeyboard = !hiddenKeyboard;
[self prepareToolbarsAndStatusbar];
}
Needless to say, although I see the button-press animation, I never see the NSLog message. And one last observation, made by accident. Changing the setAction selector to this:
[toggleKeyboardItem setAction:#selector(noSuchRoutine:)];
compiles cleanly, possibly indicating that my routine name is being ignored for some reason.
Anyone have any ideas? Thanks.
I found the answer! In button action not responding iphone, it's said that the action and target need to be set on the UIButton, not the UIBarButtonItem. I don't know if that's new with the latest version of Xcode, but I guess it is since other questions (such as the one I mention above) use a different technique. Here's my new code:
UIButton *toggleKeyboardButton = [UIButton buttonWithType:UIButtonTypeCustom];
toggleKeyboardButton.bounds = CGRectMake( 0, 0, keyboardAddImage.size.width, keyboardAddImage.size.height );
[toggleKeyboardButton setImage:keyboardAddImage forState:UIControlStateNormal];
[toggleKeyboardButton addTarget:self action:#selector(toggleKeyboard) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *toggleKeyboardItem = [[UIBarButtonItem alloc] initWithCustomView:toggleKeyboardButton];
//[toggleKeyboardItem setTarget:self];
//[toggleKeyboardItem setAction:#selector(toggleKeyboard:)];
Though its too late, but for future references, I would like to quote apple docs for the method,
- (instancetype)initWithCustomView:(UIView *)customView;
The bar button item created by this method does not call
the action method of its target in response to user interactions.
Instead, the bar button item expects the specified custom view to
handle any user interactions and provide an appropriate response.
Did you try
-(void)toggleKeyboard
and
[toggleKeyboardItem setAction:#selector(toggleKeyboard)]; without :
and it made any difference? Is the method declared in the interface file?
Here's my code (it's wrapped in an IBAction that is called when the button is pressed):
if (myButton.currentTitle == #"test") {
[myButton setTitle:#"test2" forState:UIControlStateNormal];
}
if (myButton.currentTitle == #"test2") {
[myButton setTitle:#"test" forState:UIControlStateNormal];
}
I want the UIButton text to toggle when pressed (if text = "test" then change to "test2" and when pressed if text = "test2" change to "test").
I do have an IBOutlet connected for myButton and the the IBAction connected to myButton--so I am pretty sure it isn't a problem with my connections.
For some reason this isn't working, I'm sure I am missing something very simple.
use isEqualToString: instead of ==
This is because you lack a control statement that skips the second if when the first one succeeds. When you come into the block with "test", you switch it to "test2", and then the second condition succeeds immediately, and you turn "test2" back into "#test".
You can an an else to fix this, but you can skip the if altogether by using an NSArray that maps the current state to the new state.
// This should be made static, and initialized only once
NSDictionary *nextTitle = [NSDictionary dictionaryWithObjectsAndKeys:
#"test", #"test2", #"test2", #"test", nil];
// This line does the toggling
[myButton setTitle:[nextTitle valueForKey:myButton.currentTitle] forState:UIControlStateNormal];
if ([myButton.currentTitle isEqualToString:#"test"]) {
[myButton setTitle:#"test2" forState:UIControlStateNormal];
}
if ([myButton.currentTitle isEqualToString:#"test2"]) {
[myButton setTitle:#"test" forState:UIControlStateNormal];
}
Hope, this will help you...
Comparing user-visible strings is generally considered bad practice (and becomes tedious when you need to do i18n), especially with string literals since it's vulnerable to typos.
If you're just going to toggle between two states, the easiest thing to do is to use the UIControl.selected property (corresponding to UIControlStateSelected):
// In init
[myButton setTitle:#"test" forState:UIControlStateNormal];
[myButton setTitle:#"test2" forState:UIControlStateSelected];
[myButton setTitle:#"test2" forState:UIControlStateSelected|UIControlStateHighlighted];
// Toggle
myButton.selected = !myButton.selected;
It also makes the code a lot cleaner when you when you decide to toggle the button image/background/text colours too.
Note the slight gotcha: If you don't set the title for UIControlStateSelected|UIControlStateHighlighted it will use the title for UIControlStateNormal when the button is both selected and highlighted (touched).
When comparing strings to each other try using if([str1 compare:str2] == NSOrderedSame)
I know this question is asked many a times,and i am also implementing the same funda for chanding the title of the uibutton i guess.
Let me clarify my problem first. I have one uibutton named btnType, on clicking of what one picker pops up and after selecting one value,i am hitting done button to hide the picker and at the same time i am changing the the title of the uibutton with code
[btnType setTitle:btnTitle forState:UIControlEventTouchUpInside];
[btnType setTitleColor:[UIColor redColor] forState:UIControlEventAllEvents];
But with my surpriaze,it is not changed and application crashes with signal EXC_BAD_ACCESS. I am not getting where i am making mistake.I have allocated memory to the btnType at viewdidLoad. Also I am using
-(IBAction)pressAddType
{
toolBar.hidden = FALSE;
dateTypePicker.hidden = FALSE;
}
event on pressing the button to open the picker. Also i would like to mention that i have made connection with IB with event TouchUpInside for pressAddType.
Any guesses? I will be grateful if you could help me.
Thanks.
UPDATE:
#interface AddSettingPage : UIViewController<UITextFieldDelegate>
{
IBOutlet UIButton *btnType;
NSString *btnTitle;
}
#property (nonatomic, retain) IBOutlet UIButton *btnType;
-(IBAction)pressAddType;//:(id)sender;
Also
#synthesize btnType,btnTitle;
try
[yourButton setTitle:#"your title" forState:UIControlStateNormal];
[yourButton setTitle:#"your title" forState:UIControlStateSelected];
[yourButton setTitle:#"your title" forState:UIControlStateHighlighted];
when the picker is dismissed, the button (which was the control that hold focus) will be in the selected state (or the highlighted .. check it out).
and stop using UIControlEventTouchUpInside in the forState: parameter. it is not a state, it is an event. you are passing an event identifier instead of a state identifier
The state you pass in setTitle should be something like UIControlStateNormal:
[b setTitle:#"" forState:UIControlStateNormal];
Instead of
- (IBAction) pressAddType;
declare
- (IBAction) pressAddType:(id)sender; //or (UIButton *)sender
and define it like:
-(IBAction)pressAddType:(id)sender
{
toolBar.hidden = FALSE;
dateTypePicker.hidden = FALSE;
[(UIButton *)sender setTitle:btnTitle forState:UIControlEventTouchUpInside];
[(UIButton *)sender setTitleColor:[UIColor redColor] forState:UIControlEventAllEvents];
}
As you can see, you don't need to have your button as an ivar because it is passed as a parameter of the method when pressed.
This answer why not button title change only,EXC_BAD_ACCESS error only you getting when an object you trying to access those object is not in memory of stack or that object has null value. So my advice is please check your object (btnTitle) is in memory or not?
I've been coding for a while now in objective-c and am comfortable with it... but one thing eludes me. Memory management. I'm releasing as I think is correct, but this bit of code is throwing a "EXC_BAD_ACCESS" and crashes the app.
When I comment out and DON'T release the button and image, it works fine. The function is called to read through an array of image filenames.
for (x=items_start;x<items_stop;x++) {
UIButton *button;
UIImage *buttonImage;
buttonImage = [UIImage imageNamed:[NSString stringWithFormat:#"%i.png", x]];
button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tag = x;
[button setImage:buttonImage forState:UIControlStateNormal];
[button addTarget:self action:#selector(duplicateImage:) forControlEvents:UIControlEventTouchUpInside];
[viewBasicItems addSubview:button];
[buttonImage release];
[button release];
}
any ideas? Like i said, when I comment out the last two lines (releasing the button and image) it works OK. Is this normal or should I be able to release them?
Note: I have remove a fair bit of other code to show this example in a smaller chunk!
The instance of button is autoreleased:
button = [UIButton buttonWithType:UIButtonTypeCustom];
You're using the convenience method +buttonWithType: instead of an alloc/init pair. So your app will crash here, as well:
[button release];
Either remove that -release statement or use alloc/init to instantiate the button view.
I would recommend you use alloc/init since you're doing all of this stuff inside a for loop. You could be building up a lot of objects in that loop that need to be autoreleased. It's probably better to manually allocate memory and release it.
And do read Apple's memory management guide.
buttonImage = [UIImage imageNamed:[NSString stringWithFormat:#"%i.png", x]];
Your buttonImage object is autoreleased so you must not release it in your function.
From Memory management guide:
You only own objects you created using
a method whose name begins with
“alloc” or “new” or contains “copy”
(for example, alloc, newObject, or
mutableCopy), or if you send it a
retain message.
Edit: As Alex points your button object is autoreleased also.
You have three choices:
Use alloc/init
for example;
NSString *imagePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingString:[NSString stringWithFormat:#"/%i.png",x]];
buttonImage = [[UIImage alloc] initWithContentsOfFile:imagePath];
and release it after you are done
[buttonImage release];
Or use retain/copy
buttonImage = [[UIImage imageNamed:[NSString stringWithFormat:#"%i.png", x]] retain ];
and release it
[buttonImage release];
Or use Autoreleased objects like you did but do not release it, because they will be released automatically.
You should read Apple memory management guide like others said.
Ok, the basics.
Most methods that create objects return objects that are allocated, then "autoreleased."
The autorelease call adds your object to the "autorelease pool", which means that they will receive a release call next time your app visits the event loop.
When you use auto released objects, you can use them, then forget about them. They get released automatically.
The exception, as others have said, is calls that have "init" or "new" in the name, or calls to "copy" methods. These methods return objects that have not been auto released. The owner of these objects needs to release or autorelease these objects in order for them to be deallocated, and not cause a memory leak.
In your example code, you create your buttonImage and button objects using the calls +imageNamed and +buttonWithType.
These are class calls that return an object of the desired type. Since they do not contain "init" or "new" in their names, the objects that they return are already autoreleased, so you should NOT release them.
You pass the image you create to the button, so the button retains the image. You then pass the button object to your viewBasicItems object with the -addSubview call, so the view retains the button.
Thus, you should not do anything else. The button will retain the image, and the view will retain the button.
I hope that helps.
it is not a good method of using [UIButton ButtonWithType:]..coz you cant relase the object..instead of this jus use
`for (x=items_start;x<items_stop;x++) {
UIButton *button;
UIImage *buttonImage;
buttonImage = [UIImage alloc] initWithContentOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingString:[NSString stringWithFormat:#"/%i.png",x]]];
button = [[UIButton alloc] init];
button.tag = x;
[button setImage:buttonImage forState:UIControlStateNormal];
[button addTarget:self action:#selector(duplicateImage:) forControlEvents:UIControlEventTouchUpInside];
[viewBasicItems addSubview:button];
[buttonImage release];
[button release];
}
`
Fixing in your code as following:
for (x=items_start;x<items_stop;x++) {
UIButton *button;
button = [UIButton buttonWithType:UIButtonTypeCustom];
button.tag = x;
[button setImage:[UIImage imageNamed:[NSString stringWithFormat:#"%i.png", x]]
forState:UIControlStateNormal];
[button addTarget:self action:#selector(duplicateImage:)
forControlEvents:UIControlEventTouchUpInside];
[viewBasicItems addSubview:button];
[button release];
}