How to set the titleLabel.text of a UIButton? - ios

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];
}

Related

Alternative option of UIButton addTarget:action:forControlEvents: method in IOS

I know that this question has been asked so many times but i want to pass a custom object as an argument on clicking a button.
UIButton addTarget:action:forControlEvents: doesn't allow us to do that but it is important for me so I can do further on the basis of custom objects.
If an alternative for add target is possible, so please give the solution.
The code is like this:
Custom Object:
HeaderData *cell ;
Button:
_forward =[UIButton buttonWithType:UIButtonTypeRoundedRect];
[_forward setTitle:#"F" forState:UIControlStateNormal];
_forward.frame = CGRectMake(300, (_CELL_HEIGHT-_LABEL_HEIGHT)/2, 10 ,_LABEL_HEIGHT);]
[_forward addTarget:web action:#selector(functionName:) forControlEvents:UIControlEventTouchUpInside];
and the function which is called on clicking the UIButton _forward is:
-(void)functionName:(UIButton *)sender{
//code for further programming on the basis of cell.
}
You could make a custom subclass of UIButton:
#interface CustomDataButton : UIButton
#property (nonatomic, strong) id userData;
#end
Then in functionName, you can pull the data back out:
-(void)functionName:(UIButton *)sender
{
if ([sender isKindOfClass:[CustomDataButton class]])
{
id customData = ((CustomDataButton *) sender).userData;
}
}
caveat: written without an IDE. Watch out for typos etc.

ReactiveCocoa: How do you bind a button's title to a text property?

I've got a UIButton whose title for state normal/highlighted should keep synced with a property of its container object.
How can I bind button title for specific state with a string property?
Edit:
I know that using RACObserve and change the button title in subcribeNext block is a solution.
I'm looking for something more specifically designed for UIButton like:
RACBindButtonTitle(button, property, state1, state2);
I don't know if there is some "RAC sugar" like this.
Here's a way to do it without explicit subscription.
Explicit subscription should be avoided whenever possible so that you don't have to go through the whole #weakify(self) #strongify(self) dance.
[self.button rac_liftSelector:#selector(setTitle:forState:)
withSignals:
RACObserve(self, normalButtonTitle),
[RACSignal return:#(UIControlStateNormal)],
nil];
[self.button rac_liftSelector:#selector(setTitle:forState:)
withSignals:
RACObserve(self, selectedButtonTitle),
[RACSignal return:#(UIControlStateSelected)],
nil];
liftSelector:withSignals: will subscribe eagerly to its signals, unlike many another RAC functions.
If you mean they're synced with one property, something like this:
[RACAble(self.buttonTitle) subscribeNext:^(NSString *newTitle) {
NSString *normalTitle = [NSString stringWithFormat:#"Normal %#", newTitle];
NSString *highlightedTitle = [NSString stringWithFormat:#"Highlighted %#", newTitle];
[self.button setTitle:normalTitle forState:UIControlStateNormal];
[self.button setTitle:highlightedTitle forState:UIControlStateHighlighted];
}];
If you mean there're two properties, something like this:
[RACAble(self.normalButtonTitle) subscribeNext:^(NSString *newTitle) {
[self.button setTitle:newTitle forState:UIControlStateNormal];
}];
[RACAble(self.highlightedButtonTitle) subscribeNext:^(NSString *newTitle) {
[self.button setTitle:newTitle forState:UIControlStateHighlighted];
}];
Please refer to this gist.
You can't use RAC() directly on a UIButton because of the UIKit design:
RAC(self.button.titleLabel, text) = titleSignal; // Don't do this.
One of the solutions is using dynamic property to support RAC() binding macro:
// .h
#interface UIButton (RACTitleSupport)
#property (strong, nonatomic) NSString *racExt_Title;
#end
// .m
#implementation UIButton (RACTitleSupport)
#dynamic racExt_Title;
- (void)setRacExt_Title:(NSString *)racExt_Title
{
[self setTitle:racExt_Title forState:UIControlStateNormal];
}
- (NSString *)racExt_Title
{
return [self titleForState:UIControlStateNormal];
}
#end
Now you can use RAC() binding macro like this:
RAC(self, button.racExt_Title) = titleSignal;
Cheers <3

Pass Arguments other than event & ID to #selector iOS

I am creating an iOS app as defined here! So here in this app I am using a segmented control (Dynamically added) to give the user the choice to select one of the options. When the user selects a choice I want to send a string to selector method, so I wanted to know if we can send anything apart from id and event to an action:#selector that we add.
Here's the code
NSArray *optionsArray =[NSArray arrayWithArray:qna2.answers];
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:optionsArray];
segmentedControl.frame = CGRectMake(xOffset,yOffset, 250, 50);
[segmentedControl setSelectedSegmentIndex:UISegmentedControlNoSegment];
[self.view addSubview:segmentedControl];
yOffset+=100;
[segmentedControl addTarget:self action:#selector(MyAction:) forControlEvents: UIControlEventValueChanged];
So can we do like #selector(Myaction: Category) ?
Your best bet is probably to store the string in a property like this:
#property (nonatomic, copy) NSString *myString;
Then, retrieve the value in the myAction: method.
Or you could subclass your control and add a property that way:
#interface MySegmentedControl : UISegmentedControl
#property (nonatomic, copy) NSString *stringProperty;
#end
When you set each segmented control:
MySegmentedControl *segmentedControl = [[MySegmentedControl alloc] initWith...];
segmentedControl.stringProperty = #"Some meaningful value";
[segmentedControl addTarget:self action:#selector(myAction:) forControlEvents:UIControlEventValueChanged];
 
In the change handler:
- (void) myAction:(MySegmentedControl *)sender
{
NSString *importantString = sender.stringProperty;
// do stuff with importantString
}
My approach would be to subclass UISegmentedControl, say MYUISegementedControl, and create a delegate protocol so that it can call back to your UIViewController.
When you create your MYUISegmentedControl instance you can add whatever additional properties you need and pass this information back to the delegate, or at the very least your delegate can access these properties off the MYUISegmentedControl properties.
You could also use a category rather than a subclass.

How to access User Defined Runtime Attribute from the 'sender' object?

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")] )
}

iOS - passing Sender (button) name to addSubview

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

Resources