44

I'm trying to change the title of an UIButton I've created programmatically, when the user clicks in it. So, this is my code to create the UIButton:

myButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, parentView.frame.size.width, parentView.frame.size.height)];
[myButton setBackgroundColor:[UIColor blackColor]];
[myButton setAlpha:0.7];
[myButton setTitle:@"Hello" forState:UIControlStateNormal];
[myButton addTarget:self action:@selector(userClicked:) forControlEvents:UIControlEventTouchUpInside];

[parentView addSubview:myButton];

And, in my userClicked: method, I do:

-(void) userClicked:(UIButton*)button
{
    NSLog(@"USER CLICKED!!!");
    if ([NSThread isMainThread])
    {
        NSLog(@"is main thread");
    }

    [button setTitle:@"Bye" forState:UIControlStateHighlighted];
    [button setTitle:@"Bye" forState:UIControlStateNormal];
    [button setTitle:@"Bye" forState:UIControlStateSelected];

    [self someLengthyComputation];
}

The weird thing is that I can see the log messages printed:

USER CLICKED!!! 
isMainThread

But, the title of the button does not change! What am I doing wrong?

EDIT: Setting the title for several states doesn't work either.

EDIT2: If I print the description of button in the debugger window of Xcode, it shows the right title!

Printing description of button->_titleView:
<UIButtonLabel: 0xa4c9310; frame = (95 216; 130 22); text = 'Bye'; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0xa44f080>>
neutrino
  • 2,297
  • 4
  • 20
  • 28

24 Answers24

51

This worked for me to update the title text (iOS 7.1, Xcode 5.1):

button.enabled = FALSE;
[button setTitle:@"Test" forState:UIControlStateNormal];
button.enabled = TRUE;
Walter Schurter
  • 997
  • 9
  • 14
29

I was having the same problem, and I noticed that everywhere else I was setting the attributedTitle. So any updates to the title were not affecting the attributed title.

My solution:

[button setAttributedTitle:@"" forState:UIControlStateNormal];

instead of

[button setTitle:@"" forState:UIControlStateNormal];
Jeffrey Sun
  • 7,789
  • 1
  • 24
  • 17
  • 2
    This was the issue for me: I'd referred to the button's title using an attributed string earlier to adjust tracking, and it would no longer update if I simply used setTitle. Note that you can't pass a simple string, you need something like this: [_myButton setAttributedTitle:[[NSAttributedString alloc] initWithString:@"New title for button" attributes:nil] forState:UIControlStateNormal]; – CraicDesign Dec 06 '14 at 05:08
  • I'm on XCode 9.4.1 / swift and I had the same issue. setAttributedTitle was the only solution that worked for me – budiDino Oct 01 '18 at 16:34
21

At the moment, the shortest work around I came with is calling:

[button setNeedsLayout];

After updating the title.

This seems to happen in iOS 7.1. All my buttons, which were behaving correctly in previous iOS versions (or maybe just compiled with previous SDKs) suddenly stopped doing that when compiled in Xcode 5.1.1 SDK 7.1.

Looks like Apple's bug.

Lukasz
  • 19,816
  • 17
  • 83
  • 139
12

I had a similar problem using storyboards. Using the answers above I had to use

[mybutton setTitle:@"SomeText" forState:UIControlStateNormal];
[button setNeedsLayout];
[button layoutIfNeeded];

AND I had to make sure that the button type was 'Custom' not 'System' in the attributes inspector.

user3000868
  • 455
  • 4
  • 13
  • 1
    With that AND ... comment you saved my day !!! Thank you. That was it - never forget to set the button to "custom" in storyboard, when you subclass, otherwise the behaviour might be weird. – Vilém Kurz Oct 06 '16 at 14:59
  • 1
    Thanks thanks thanks. I was breaking my head for past 1 week. Your "And" part made my day.. – Swayambhu Feb 08 '17 at 08:00
  • 1
    I only needed to set the type to Custom, I didn't need to add the setNeedsLayout or layoutIfNeeded. – christophercotton May 31 '17 at 19:28
  • Thanks, you saved my day pointing out the button style in storyboard! My problem actually was that the button style set in storyboard was System and not Custom :) – Eugenio Jun 19 '18 at 14:52
  • [button setNeedsLayout]; and [button layoutIfNeeded]; are same thing except for one difference. First one work asynchronously and second one work synchronously. So either of them works fine, – Rehan Ali Aug 05 '18 at 09:42
8

Please see if this might help you...when the button is clicked check for condition if buttonToggled...like below when you have a function like changeButtonText

-(IBAction)changeButtonText:(id)sender {
    if (!buttonToggled) {
        [sender setTitle:@"Initial Text" forState:UIControlStateNormal];
        buttonToggled = YES;
    } else {
        [sender setTitle:@"Alternative Text" forState:UIControlStateNormal];
        buttonToggled = NO;
    }
}
Corwin Newall
  • 508
  • 8
  • 18
bachman
  • 690
  • 7
  • 22
8

For Swift 3 to 5 just use the following:

button.setTitle("My title", for: .normal)

or for a attributed text use this:

button.setAttributedTitle(<AttributedString>, for: .normal)
zero3nna
  • 2,770
  • 30
  • 28
6

There are several issues in your code:

You are assigning callback to the button:

@selector(userClicked:)

but your code is in another method:

-(void)userTapOnTapToRefreshView:(UIButton*)button

To fix that you need to implement something like this:

-(void)userClicked:(id)sender
{
    [(UIButton*)sender setTitle:@"Bye" forState:UIControlStateNormal];
}

Also this part of code does not make sense for me:

[parentView myButton];

Try to change it to:

[parentView addSubview:myButton];

Hope it will help :)

Eugene Tartakovsky
  • 1,594
  • 17
  • 22
  • Thank you very much for your answer. In fact, all those errors have been made by me when trying to strip down my real code to post here my question. I've edited my original post, to show the correct code with the changes you pointed out. – neutrino Nov 14 '13 at 09:41
5

This is kinda late and somewhat relates to Walter Schurter's response:

I had a similar issue where my button text was being set correctly until I updated to 7.1. Then I found that since my button was disabled, I had to set the title color and title text for the disabled state to get the text to show up. Then everything worked like a charm!

dragonflyesque
  • 485
  • 5
  • 11
5

In iOS 7, UIButton's title is not updated when it is disabled. It seems like a bug on iOS 7.

As a workaround, update both normal and disabled title. It works!

[button setTitle:title forState:UIControlStateNormal];
[button setTitle:title forState:UIControlStateDisabled];
ccoroom
  • 699
  • 9
  • 23
4

Try this:

[button setTitle:@"Bye" forState:UIControlStateHighlighted];

or:

[button setTitle:@"Bye" forState:UIControlStateSelected];

You could also modify your void function:

-(void) userClicked
{
    NSLog(@"USER CLICKED!!!");
    if ([NSThread isMainThread])
    {
        NSLog(@"is main thread");
    }

    [myButton setTitle:@"Bye" forState:UIControlStateNormal];
}
BenMorel
  • 34,448
  • 50
  • 182
  • 322
Nikos M.
  • 13,685
  • 4
  • 47
  • 61
  • Thanks! `[button setTitle:@"Bye" forState:UIControlStateHighlighted];` did the trick! – neutrino Nov 14 '13 at 09:32
  • OK, My mistake, even if I set the title for `UIControlStateNormal`, `UIControlStateSelected` and `UIControlStateHighlighted`, the title does not change. I will update my question with my actual code. – neutrino Nov 14 '13 at 09:45
  • Edited my answer too. Declare myButton as a property so you can refer to it in the void function. – Nikos M. Nov 14 '13 at 09:48
  • not working :( , the issue is not related to mainthread , because you can update the button if you didn't navigate to another Uiviewcontroller – Amr Angry Aug 21 '16 at 15:00
4

In my case I tried everything but nothing was working. Then I just changed button type from system to custom from storyboard. BOOM! everything started behaving normally.

Siddhesh Mahadeshwar
  • 1,591
  • 14
  • 16
4

Change the button type to 'Custom', instead of 'System' and it will work as expected :)

3

Well, is all about the enabled state of a UIButton, Apple has changed something in 7.1 that does not allow you to change the title if you have the UIButton on a disabled state, thats all.

Thanks Apple, i have lost 10 min. debuging an app that was working fine.

Just found out this morning, got updated XCode yesterday to the 5.1.1 and iOS to 7.1

Kiko Seijo
  • 31
  • 3
3

It could be the button layout refresh issue..... Try using...

[button setNeedsLayout];
[button layoutIfNeeded];

It will force button to update the layout.

iOSGuest
  • 61
  • 1
2

Finally, I've figured it out. There were two problems:

1) button was not in state UIControlStateNormal.

2) I was calling a method performing a long computation just after setting the title, [self someLengthyComputation].

I've solved the problem by:

1) Setting the title for all states of the button.

2) Performing that big computation in another thread, not the main thread.

My working code now is:

-(void) userClicked:(UIButton*)button
{
    NSLog(@"USER CLICKED!!!");
    if ([NSThread isMainThread])
    {
        NSLog(@"is main thread");
    }

    [button setTitle:@"Bye" forState:UIControlStateHighlighted];
    [button setTitle:@"Bye" forState:UIControlStateNormal];
    [button setTitle:@"Bye" forState:UIControlStateSelected];
    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
        [self someLengthyComputation];
    });
}

Thank you very much to everybody who has answered/commented!

neutrino
  • 2,297
  • 4
  • 20
  • 28
  • not working :( ,the issue is not related to mainthread , because you can update the button if you didn't navigate to another Uiviewcontroller – Amr Angry Aug 21 '16 at 15:01
2

This may be trivial, but if you set some button image (instead of background image) which fills the whole button frame, this will shift the title right and thus make it invisible. If so, try changing button image to background image.

dobranoc
  • 465
  • 1
  • 4
  • 17
1

Per Apple developer guide, you should not set either the button title label text or color directly as a property (for example, do not do this: myButton.titleLabel.text=@"SomeText" or myButton.titleLabel.textColor=[UIColor blackColor]).

Rather, you set them using the UIButton setter functionality, like this:

    [myButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];

...or like this:

    [myButton setTitle:@"SomeText" forState:UIControlStateNormal];.

See the Apple guide.

1

There is no requirement to use:

[button setNeedsLayout];
[button layoutIfNeeded];

Instead, first set type as DEFAULT to CUSTOM

If you applied setAttributedTitle then use:

[button setAttributedTitle:[NSAttributedString new] forState:UIControlStateNormal];

Otherwise there is no need to change anything.

If the color of the text has not changed then apply same thing and set title color with:

[button setTitleColor:[any_color] forState:UIControlStateNormal];
shim
  • 9,289
  • 12
  • 69
  • 108
jenish
  • 268
  • 3
  • 11
1

Conclusion after trying many solutions is to use setAttributedTitle instead of setTitle.

To make the title string for AttributedString:

NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:@" new Test "];

[button setAttributedTitle:attString forState:UIControlStateNormal];

By the way, this problem is not occasionally happen in normal but it suddenly happen mainly for the following reasons:

  1. If you change the enabled state of a UIButton and try to change the title.

  2. If you use an attributed value and then want to change it using setTitle, the attributed value is superior to the title in that case.

  3. If you navigate to another view controller and then return back to update the button title.

shim
  • 9,289
  • 12
  • 69
  • 108
Amr Angry
  • 3,711
  • 1
  • 45
  • 37
1

None of the above worked for my case (I was having a button on top of a view presented with UIPopoverController), and I had to removeFromSuperview before setTitle, then addSubview back - hope this helps someone with similar issues

hyouuu
  • 2,471
  • 2
  • 27
  • 37
0

I doubt if 'button' passed in as a parameter is myButton.Anyway,if myButton is a member var,you can just [myButton setTitle:@"Bye" forState:UIControlStateNormal];

Hunter
  • 136
  • 1
  • 9
  • I've checked that, and button was in fact equals to myButton. The problem was in the button state. See accepted answer. Thank you anyway! – neutrino Nov 14 '13 at 09:35
  • I thought you would change the text after tap the button forever.Anyway,congratulations. – Hunter Nov 14 '13 at 09:41
0

Try to declare you button as a property, either in the interface or in the implementation part of you view / view controller

@property(nonatomic,strong) UIButton* myButton;

then create it

self.myButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, parentView.frame.size.width, parentView.frame.size.height)];
[_myButton setBackgroundColor:[UIColor blackColor]];
...

It should work. Don't ask me why really - I just think it's all about ARC / Modern memory management..

EDIT:

It should also work if you keep a reference on the button in your implementation..

@implementation myViewController
{
    UIButton* myButton;
}

-(void)createButton
{
    myButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, parentView.frame.size.width, parentView.frame.size.height)];
    [myButton setBackgroundColor:[UIColor blackColor]];
    ...
}

...
Moose
  • 2,607
  • 24
  • 23
0

instead of yourButton.titleLabel?.text = "T"

use this yourButton.setTitle("TITLE", for: .normal)

Divyanshu Kumar
  • 1,272
  • 15
  • 15
-1

Always do addSubview before setting the text on UIButton.

PPVZ
  • 17