I have a UIButton in a Storyboard scene. The button has a User-Defined-RunTime-Attribute 'type'(String) configured. When pressed the button calls
-(IBAction)pressedButton:(id)sender
Will I be able to access the User-Defined-RunTime-Attribute from 'sender'?
Yes:
-(IBAction)pressedButton:(id)sender
{
id value = [sender valueForKey:key];
}
Note that you cannot use a User Defined Run Time attribute, unless you subclass UIButton and add it as a strong property, for example
#interface UINamedButton : UIButton
#property (strong) NSString *keyName;
#end
If you set a User Defined Run Time attribute, and you have not done this, Xcode will badly crash unfortunately.
You can then get that value like
-(IBAction)clicked:(UIControl *)sender
{
NSString *test = #"???";
if ( [sender respondsToSelector:#selector(keyName)] )
test = [sender valueForKey:#"keyName"];
NSLog(#"the value of keyName is ... %#", test);
// if you FORGOT TO SET the keyName value in storyboard, that will be NULL
// if it's NOT a UINamedButton button, you'll get the "???"
// and for example...
[self performSegueWithIdentifier:#"idUber" sender:sender];
// ...the prepareForSegue could then use that value in the button.
// note that a useful alternative to
// if ( [sender respondsToSelector:#selector(stringTag)] )
// is...
// if ( [sender respondsToSelector:NSSelectorFromString(#"stringTag")] )
}
Related
I add Tag for the each buttons in cell(button is single), when is built TableView as:
[cell.hiddenButtonWithIdCell setTag:(int) self.responseObject[#"results"][indexPath.row][#"id"]]; // Value is 4
Where hiddenButtonWithIdCell is button name (Outlet property):
#property (weak, nonatomic) IBOutlet UIButton *hiddenButtonWithIdCell;
Important: outlet in the file cell.h
At file where I loop table I have (so event action here):
Important: (Both the methods in the same file)
- (IBAction)openDetailsOfDish:(id)sender {
NSLog(#"Click: %#", [sender tag]); // Here I get correct Tag and equal 20
[self performSegueWithIdentifier:#"Full" sender:sender];
}
But at:
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(UIButton*) sender {
NSLog(#"%#", sender.tag); // Here is wrong tag
}
sender.tag is NSInterger so you should log this like that:
NSLog(#"MY SENDER TAG %lu", sender.tag);
Check here for more info about using button's tag with segue:
How to pass prepareForSegue: an object
I have a property in my view controller (the app only has one view controller)
#property (weak, nonatomic) NSString * correctAnswer;
In viewDidLoad, I set that property like this and the log statement confirms that it's being set
self.correctAnswer = info.correctAnswer;
NSLog(#"correctAnswer %#", self.correctAnswer);
However, when I click a button in the view and try to inspect that same property in the action (in the same view controller) triggered by the button, it's turning out to be (null) Therefore the string comparison is always wrong. Can you explain what I'm doing wrong?
- (IBAction)checkResponse:(UIButton *)sender {
UIButton *resultButton = (UIButton *)sender;
NSLog(#" The button's title is %#.", resultButton.currentTitle);
NSLog(#"correct answer %#", self.correctAnswer); //null
if ([resultButton.currentTitle isEqualToString:self.correctAnswer]){
NSLog(#"you guessed right");
}else {
NSLog(#"You guessed wrong");
}
}
Change the property from weak to strong (or better yet in this case, copy).
Weak properties are set to nil as soon as the referenced object is deallocated.
I would like to know how to do that properly because I am getting a Bad Access error.
In my app I have 81 UIButtons with an IBAction attached to all of them by Interface Builder, this IBAction should set the text of the button the user tapped on. I am trying to do that this way:
- (IBAction)changeTitle:(UIButton *)button {
button.titleLabel.text = [NSString stringWithFormat:#"%#", myString];
}
-(IBAction)setMyString:(id)sender{
myString = [NSString stringWithFormat:#"text"];
}
However this comes into a Bad Access Error, how could I solve that?
Million Thanks!
error message: EXC_BAD_ACCESS (code=1, address=0x0)
(lldb)
You should not try to set the label text directly, but use UIButton setTitle:forState::
- (IBAction)changeTitle:(UIButton *)sender {
[button setTitle:myString forState:UIControlStateNormal];
}
The label property can be used to configure things like the font and other properties of the label, but some of them (color, shadow color and text) have to be set using the UIButton methods.
You might have a memory management problem. You are not using a #property to set your instance variable. You are setting it manually so you have to manage the memory yourself.
-(IBAction)setMyString:(id)sender{
[myString release]; // release old instance
myString = [[NSString stringWithFormat:#"text"] retain];
}
or even better, create a #property for your variable if you haven't done so already and set your variable by using the setter. Like this:
#property (copy, nonatomic) NSString *string; // in your #interface
#synthesize string = myString; // in your #implementation
-(IBAction)setMyString:(id)sender{
self.string = [NSString stringWithFormat:#"text"]; // setter will release old value, and retain new value
}
- (IBAction)changeTitle:(UIButton *)button {
// you should not set the text of the titleLabel directly
[button setTitle:self.string forState:UIControlStateNormal];
}
I'm a beginner working on an emotion annotation application. There are several buttons like "happy","angry" and etc... All the buttons will call a same action. And there is an UISlider. If you click on "happy" , then you adjust the slider to annotate how happy you are now. After that you click on button "angry", the current value of slider will be stored in an float variable relatived to the last button, like "happy". Then you adjust the same slider to annotate how "angry" you are. And the next button....
I don't have idea about how to store the slider value for the last button...
Are there any ideas?
Thank you very much!!
There are many ways to approach this. One of the most simple solutions is to tag your buttons, and then use a method to work out which button the action came from, set up a dictionary object with the slider value inside it, and then write it to a strong array accordingly.
MainViewController.h
int emotionNumber
#property (strong, nonatomic) NSMutableArray *array;
//Declare your slider and buttons
MainViewController.m
#implementation
#synthesise array;
- (void)viewDidLoad {
[happyButton setTag:0];
[angryButton setTag:1];
array = [[NSMutableArray alloc] initWithCapacity:X]; <---- number of emotions
}
- (IBAction)setValue:(id)sender {
// You will also want to keep track of emotionNumber (the int) here, and modify the
//code below to write it to the correct place in the array.
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
UIButton *button = (UIButton *)sender;
switch (button.tag) {
case 0:
[dictionary setValue:slider.value forKey:#"angry"];
[array addObject:dictionary];
break;
case 1:
[dictionary setValue:slider.value forKey:#"happy"];
[array addObject:dictionary];
break;
default:
break;
}
}
I want to pass a variable to a UIButton action, for example
NSString *string=#"one";
[downbutton addTarget:self action:#selector(action1:string)
forControlEvents:UIControlEventTouchUpInside];
and my action function is like:
-(void) action1:(NSString *)string{
}
However, it returns a syntax error.
How to pass a variable to a UIButton action?
Change it to read:
[downbutton addTarget:self action:#selector(action1:) forControlEvents:UIControlEventTouchUpInside];
I don't know about the Iphone SDK, but the target of a button action probably receives an id (usually named sender).
- (void) buttonPress:(id)sender;
Within the method call, sender should be the button in your case, allowing you to read properties such as it's name, tag, etc.
If you need to distinguish between multiple buttons, then you could mark your buttons with tags like this:
[downbutton addTarget:self action:#selector(buttonPress:) forControlEvents:UIControlEventTouchUpInside];
downButton.tag = 15;
In your action delegate method you can then handle each button according to its previously set tag:
(void) buttonPress:(id)sender {
NSInteger tid = ((UIControl *) sender).tag;
if (tid == 15) {
// deal with downButton event here ..
}
//...
}
UPDATE: sender.tag should be a NSInteger instead of a NSInteger *
You can use associative references to add arbitrary data to your UIButton:
static char myDataKey;
...
UIButton *myButton = ...
NSString *myData = #"This could be any object type";
objc_setAssociatedObject (myButton, &myDataKey, myData,
OBJC_ASSOCIATION_RETAIN);
For the policy field (OBJC_ASSOCIATION_RETAIN) specify the appropriate policy for your case.
On the action delegate method:
(void) buttonPress:(id)sender {
NSString *myData =
(NSString *)objc_getAssociatedObject(sender, &myDataKey);
...
}
Another option for passing variables, which I find to be more direct than the tag from leviatan's answer is to pass a string in the accessibilityHint. For example:
button.accessibilityHint = [user objectId];
Then in the action method of the button:
-(void) someAction:(id) sender {
UIButton *temp = (UIButton*) sender;
NSString *variable = temp.accessibilityHint;
// anything you want to do with this variable
}
The only way I've found to do this is set an instance variable before calling the action
You can extends UIButton and add a custom property
//UIButtonDictionary.h
#import <UIKit/UIKit.h>
#interface UIButtonDictionary : UIButton
#property(nonatomic, strong) NSMutableDictionary* attributes;
#end
//UIButtonDictionary.m
#import "UIButtonDictionary.h"
#implementation UIButtonDictionary
#synthesize attributes;
#end
You can set tag of the button and access it from sender in action
[btnHome addTarget:self action:#selector(btnMenuClicked:) forControlEvents:UIControlEventTouchUpInside];
btnHome.userInteractionEnabled = YES;
btnHome.tag = 123;
In the called function
-(void)btnMenuClicked:(id)sender
{
[sender tag];
if ([sender tag] == 123) {
// Do Anything
}
}
You can use the strings of the UIControlStates that you dot'n use:
NSString *string=#"one";
[downbutton setTitle:string forState:UIControlStateApplication];
[downbutton addTarget:self action:#selector(action1:) forControlEvents:UIControlEventTouchUpInside];
and the action function:
-(void)action1:(UIButton*)sender{
NSLog(#"My string: %#",[sender titleForState:UIControlStateApplication]);
}