391

I have the following code...

UILabel *buttonLabel = [[UILabel alloc] initWithFrame:targetButton.bounds];
buttonLabel.text = @"Long text string";
[targetButton addSubview:buttonLabel];
[targetButton bringSubviewToFront:buttonLabel];

...the idea being that I can have multi-line text for the button, but the text is always obscured by the backgroundImage of the UIButton. A logging call to show the subviews of the button shows that the UILabel has been added, but the text itself cannot be seen. Is this a bug in UIButton or am I doing something wrong?

Naresh
  • 16,698
  • 6
  • 112
  • 113
Owain Hunt
  • 4,619
  • 2
  • 20
  • 10

33 Answers33

701

For iOS 6 and above, use the following to allow multiple lines:

button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
// you probably want to center it
button.titleLabel.textAlignment = NSTextAlignmentCenter; // if you want to 
[button setTitle: @"Line1\nLine2" forState: UIControlStateNormal];

For iOS 5 and below use the following to allow multiple lines:

button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
// you probably want to center it
button.titleLabel.textAlignment = UITextAlignmentCenter;
[button setTitle: @"Line1\nLine2" forState: UIControlStateNormal];

2017, for iOS9 forward,

generally, just do these two things:

  1. choose "Attributed Text"
  2. on the "Line Break" popup select "Word Wrap"
Fattie
  • 27,874
  • 70
  • 431
  • 719
jessecurry
  • 22,068
  • 8
  • 52
  • 44
  • 5
    Please note these assignments are deprecated in iOS 6. See the answer below by @NiKKi for the updated syntax. – Aaron Brager Jan 09 '13 at 22:36
  • 6
    Just so people know: this does not work if you're using NSAttributedString – The dude Oct 29 '13 at 12:08
  • 31
    Actually, just add button.titleLabel.numberOfLines = 0; That will make the lines unlimited. And button.titleLabel.textAlignment = NSTextAlignmentLeft; if you want left justified text as the title is centered by default. – RyJ Aug 21 '14 at 16:00
  • 4
    In swift `button.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping` – Robert Jan 25 '15 at 17:36
  • @jessecurry I don't see any difference to this snippet compared with the one from NiKKi..... and NSTextAlignmentCenter isn't deprecated like NiKKi tells. – Alex Cio May 08 '15 at 11:43
  • 1
    Thanks @Robert for the swift solution. Can also use inference and omit the `NSLineBreakMode`, like so: `button.titleLabel?.lineBreakMode = .ByWordWrapping`. – mjswensen Aug 18 '15 at 22:49
  • 1
    As of Xcode 7.3, trying to center the text in IB doesn't work so you'll need to IBOutlet the button and in the viewDidLoad add: button.titleLabel.textAlignment = UITextAlignmentCenter; – Avi Cohen Apr 07 '16 at 08:31
  • 2
    Note that, if you want to use AutoLayout, `UIButton` has a bug that prevents the intrinsic content size to work properly with edge insets. To fix that, please refer to http://stackoverflow.com/questions/17800288/autolayout-intrinsic-size-of-uibutton-does-not-include-title-insets where a good solution is presented. – DrMickeyLauer Aug 15 '16 at 10:51
  • For your record, using a multi-line title label with an attributed string needs creating constraints to wrap perfectly this text inside the button: it's not as easy as a simple string. – XLE_22 Jul 02 '19 at 07:26
158

The selected answer is correct but if you prefer to do this sort of thing in Interface Builder you can do this:

pic

Adam Waite
  • 19,175
  • 22
  • 126
  • 148
61

If you want to add a button with the title centered with multiple lines, set your Interface Builder's settings for the button:

[here]

Jamal
  • 763
  • 7
  • 22
  • 32
user5440039
  • 615
  • 6
  • 7
  • Can you add a textual description of how to achieve this. An image is great, but if it becomes unavailable your answer will be useless. – Rory McCrossan Oct 21 '15 at 09:21
  • 1
    @RoryMcCrossan Hi, i added an image above. Can you view it? Please note, Xcode 7 is still buggy so button title might disappear. However, once you run the app you will get the desired result. – user5440039 Oct 21 '15 at 10:03
  • 2
    This is the best answer in this thread, actually. Works immediately and shows centered text from IB. Thanks! – RainCast Oct 30 '16 at 23:00
  • @user5440039 Thanks for the great answer. There's no need to add a textual description. – Fattie Dec 07 '16 at 15:11
54

For IOS 6 :

button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
button.titleLabel.textAlignment = NSTextAlignmentCenter;

As

UILineBreakModeWordWrap and UITextAlignmentCenter

are deprecated in IOS 6 onwards..

NiKKi
  • 3,296
  • 3
  • 29
  • 39
  • Inside `NSText.h` in the `Foundation` there is no deprecated added. Its available from 6.0 but not deprecated. – Alex Cio May 08 '15 at 11:42
  • In swift: `button.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping` `button.titleLabel?.textAlignment = NSTextAlignment.Center` – jcity Dec 02 '15 at 00:56
32

To restate Roger Nolan's suggestion, but with explicit code, this is the general solution:

button.titleLabel?.numberOfLines = 0
ma11hew28
  • 121,420
  • 116
  • 450
  • 651
baudot
  • 1,618
  • 20
  • 33
23

SWIFT 3

button.titleLabel?.lineBreakMode = .byWordWrapping
button.titleLabel?.textAlignment = .center  
button.setTitle("Button\nTitle",for: .normal)
Community
  • 1
  • 1
Md. Ibrahim Hassan
  • 5,359
  • 1
  • 25
  • 45
21

I had an issue with auto-layout, after enabling multi-line the result was like this:
enter image description here
so the titleLabel size doesn't affect the button size
I've added Constraints based on contentEdgeInsets (in this case contentEdgeInsets was (10, 10, 10, 10)
after calling makeMultiLineSupport():
enter image description here
hope it helps you (swift 5.0):

extension UIButton {

    func makeMultiLineSupport() {
        guard let titleLabel = titleLabel else {
            return
        }
        titleLabel.numberOfLines = 0
        titleLabel.setContentHuggingPriority(.required, for: .vertical)
        titleLabel.setContentHuggingPriority(.required, for: .horizontal)
        addConstraints([
            .init(item: titleLabel,
                  attribute: .top,
                  relatedBy: .greaterThanOrEqual,
                  toItem: self,
                  attribute: .top,
                  multiplier: 1.0,
                  constant: contentEdgeInsets.top),
            .init(item: titleLabel,
                  attribute: .bottom,
                  relatedBy: .greaterThanOrEqual,
                  toItem: self,
                  attribute: .bottom,
                  multiplier: 1.0,
                  constant: contentEdgeInsets.bottom),
            .init(item: titleLabel,
                  attribute: .left,
                  relatedBy: .greaterThanOrEqual,
                  toItem: self,
                  attribute: .left,
                  multiplier: 1.0,
                  constant: contentEdgeInsets.left),
            .init(item: titleLabel,
                  attribute: .right,
                  relatedBy: .greaterThanOrEqual,
                  toItem: self,
                  attribute: .right,
                  multiplier: 1.0,
                  constant: contentEdgeInsets.right)
            ])
    }

}
Amir Khorsandi
  • 3,542
  • 1
  • 34
  • 38
  • Works perfect in swift 5. I also added my function to center text in button. func centerTextInButton() { guard let titleLabel = titleLabel else { return } titleLabel.textAlignment = .center } – ShadeToD Sep 18 '19 at 14:15
  • I found adding constraint is not necessary, just set the text after lineBreakMode/textAlignment/numberOfLines are set. And also set titleEdgeInsets for top and bottom inset. – Bill Chan Mar 04 '20 at 18:25
  • @BillChan unfortunately that doesn’t work in all cases – Amir Khorsandi Mar 04 '20 at 18:30
  • 2
    As written, these constraints result in auto-layout conflict every time. Although I am not yet 100% sure of the importance of the use of inequality constraints, I do know that 2 of the constraints are written incorrectly: the bottom and right edges should use `relatedBy: .lessThanOrEqual` and flip the constant negative `constant: -contentEdgeInsets.{bottom|right}` – androidguy Jul 05 '20 at 06:11
  • Thank you - I had a `...` in the last line due to an invisible character so I have added `.lineBreakMode = .byClipping` – user2330482 Jul 23 '20 at 22:13
15

In Xcode 9.3 you can do it by using storyboard like below,

enter image description here

You need to set button title textAlignment to center

button.titleLabel?.textAlignment = .center

You don't need to set title text with new line (\n) like below,

button.setTitle("Good\nAnswer",for: .normal)

Simply set title,

button.setTitle("Good Answer",for: .normal)

Here is the result,

enter image description here

Nazrul Islam
  • 748
  • 8
  • 16
11

There is a much easier way:

someButton.lineBreakMode = UILineBreakModeWordWrap;

(Edit for iOS 3 and later:)

someButton.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
Wienke
  • 3,723
  • 27
  • 40
Valerii Hiora
  • 1,542
  • 13
  • 8
  • 4
    UIButton.lineBreakMode was deprecated in 3.0, so that's no longer a good option. – Christopher Pickslay Jan 17 '11 at 23:12
  • 2
    lineBreakMode is deprecated only as a direct property of UIButton. We are directed to "Use the lineBreakMode property of the titleLabel instead." I edited Valerii's answer accordingly. It's still a good option. – Wienke Oct 26 '12 at 18:57
11

Left align on iOS7 with autolayout:

button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
button.titleLabel.textAlignment = NSTextAlignmentLeft;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
neoneye
  • 50,398
  • 25
  • 166
  • 151
7

First of all, you should be aware that UIButton already has a UILabel inside it. You can set it using –setTitle:forState:.

The problem with your example is that you need to set UILabel's numberOfLines property to something other than its default value of 1. You should also review the lineBreakMode property.

Rog
  • 17,070
  • 9
  • 50
  • 73
  • I'm aware of the title property, but as far as I can tell it is impossible to set it to use more than one line, hence this approach. If I disable the backgroundImage, my UILabel show up, which to me suggests a bug in either bringSubviewToFront, or UIButton itself. – Owain Hunt Mar 03 '09 at 11:43
6

Swift 5 , For multi Line text in UIButton

  let button = UIButton()
  button.titleLabel?.lineBreakMode = .byWordWrapping
  button.titleLabel?.textAlignment = .center
  button.titleLabel?.numberOfLines = 0 // for Multi line text
Manikandan
  • 1,195
  • 8
  • 26
6

To fix title label's spacing to the button, set titleEdgeInsets and other properties before setTitle:

    let button = UIButton()
    button.titleLabel?.lineBreakMode = .byWordWrapping
    button.titleLabel?.numberOfLines = 0
    button.titleEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 20, right: 20)
    button.setTitle("Dummy button with long long long long long long long long title", for: .normal)

P.S. I tested setting titleLabel?.textAlignment is not necessary and the title aligns in .natural.

Guntis Treulands
  • 4,764
  • 2
  • 50
  • 72
Bill Chan
  • 3,199
  • 36
  • 32
4

For those who are using Xcode 4's storyboard, you can click on the button, and on the right side Utilities pane under Attributes Inspector, you'll see an option for Line Break. Choose Word Wrap, and you should be good to go.

Jack
  • 5,264
  • 7
  • 34
  • 43
4

Answers here tell you how to achieve multiline button title programmatically.

I just wanted to add that if you are using storyboards, you can type [Ctrl+Enter] to force a newline on a button title field.

HTH

4

Setting lineBreakMode to NSLineBreakByWordWrapping (either in IB or code) makes button label multiline, but doesn't affect button's frame.

If button has dynamic title, there is one trick: put hidden UILabel with same font and tie it's height to button's height with layout; when set text to button and label and autolayout will make all the work.

Note

Intrinsic size height of one-line button is bigger than label's, so to prevent label's height shrink it's vertical Content Hugging Priority must be greater than button's vertical Content Compression Resistance.

Varrry
  • 2,647
  • 1
  • 13
  • 27
4

You have to add this code:

buttonLabel.titleLabel.numberOfLines = 0;
JimHawkins
  • 4,843
  • 8
  • 35
  • 55
Pablo Blanco
  • 679
  • 8
  • 14
4

These days, if you really need this sort of thing to be accessible in interface builder on a case-by-case basis, you can do it with a simple extension like this:

extension UIButton {
    @IBInspectable var numberOfLines: Int {
        get { return titleLabel?.numberOfLines ?? 1 }
        set { titleLabel?.numberOfLines = newValue }
    }
}

Then you can simply set numberOfLines as an attribute on any UIButton or UIButton subclass as if it were a label. The same goes for a whole host of other usually-inaccessible values, such as the corner radius of a view's layer, or the attributes of the shadow that it casts.

Ash
  • 9,064
  • 3
  • 48
  • 59
3

If you use auto-layout on iOS 6 you might also need to set the preferredMaxLayoutWidth property:

button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
button.titleLabel.textAlignment = NSTextAlignmentCenter;
button.titleLabel.preferredMaxLayoutWidth = button.frame.size.width;
vially
  • 1,516
  • 16
  • 22
3

In Swift 5.0 and Xcode 10.2

//UIButton extension
extension UIButton {
     //UIButton properties
     func btnMultipleLines() {
         titleLabel?.numberOfLines = 0
         titleLabel?.lineBreakMode = .byWordWrapping
         titleLabel?.textAlignment = .center        
     }
}

In your ViewController call like this

button.btnMultipleLines()//This is your button
Naresh
  • 16,698
  • 6
  • 112
  • 113
3

As to Brent's idea of putting the title UILabel as sibling view, it doesn't seem to me like a very good idea. I keep thinking in interaction problems with the UILabel due to its touch events not getting through the UIButton's view.

On the other hand, with a UILabel as subview of the UIButton, I'm pretty confortable knowing that the touch events will always be propagated to the UILabel's superview.

I did take this approach and didn't notice any of the problems reported with backgroundImage. I added this code in the -titleRectForContentRect: of a UIButton subclass but the code can also be placed in drawing routine of the UIButton superview, which in that case you shall replace all references to self with the UIButton's variable.

#define TITLE_LABEL_TAG 1234

- (CGRect)titleRectForContentRect:(CGRect)rect
{   
    // define the desired title inset margins based on the whole rect and its padding
    UIEdgeInsets padding = [self titleEdgeInsets];
    CGRect titleRect = CGRectMake(rect.origin.x    + padding.left, 
                                  rect.origin.x    + padding.top, 
                                  rect.size.width  - (padding.right + padding.left), 
                                  rect.size.height - (padding.bottom + padding].top));

    // save the current title view appearance
    NSString *title = [self currentTitle];
    UIColor  *titleColor = [self currentTitleColor];
    UIColor  *titleShadowColor = [self currentTitleShadowColor];

    // we only want to add our custom label once; only 1st pass shall return nil
    UILabel  *titleLabel = (UILabel*)[self viewWithTag:TITLE_LABEL_TAG];


    if (!titleLabel) 
    {
        // no custom label found (1st pass), we will be creating & adding it as subview
        titleLabel = [[UILabel alloc] initWithFrame:titleRect];
        [titleLabel setTag:TITLE_LABEL_TAG];

        // make it multi-line
        [titleLabel setNumberOfLines:0];
        [titleLabel setLineBreakMode:UILineBreakModeWordWrap];

        // title appearance setup; be at will to modify
        [titleLabel setBackgroundColor:[UIColor clearColor]];
        [titleLabel setFont:[self font]];
        [titleLabel setShadowOffset:CGSizeMake(0, 1)];
        [titleLabel setTextAlignment:UITextAlignmentCenter];

        [self addSubview:titleLabel];
        [titleLabel release];
    }

    // finally, put our label in original title view's state
    [titleLabel setText:title];
    [titleLabel setTextColor:titleColor];
    [titleLabel setShadowColor:titleShadowColor];

    // and return empty rect so that the original title view is hidden
    return CGRectZero;
}

I did take the time and wrote a bit more about this here. There, I also point a shorter solution, though it doesn't quite fit all the scenarios and involves some private views hacking. Also there, you can download an UIButton subclass ready to be used.

jpedroso
  • 589
  • 3
  • 4
2

If you use auto-layout.

button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.numberOfLines = 2
Sourabh Sharma
  • 8,222
  • 5
  • 68
  • 78
2

swift 4.0

btn.titleLabel?.lineBreakMode = .byWordWrapping
btn.titleLabel?.textAlignment = .center
btn.setTitle( "Line1\nLine2", for: .normal)
ingconti
  • 10,876
  • 3
  • 61
  • 48
2

It works perfectly.

Add to use this with config file like Plist, you need to use CDATA to write the multilined title, like this:

<string><![CDATA[Line1
Line2]]></string>
makiko_fly
  • 546
  • 5
  • 8
1

Roll your own button class. It's by far the best solution in the long run. UIButton and other UIKit classes are very restrictive in how you can customize them.

1

In iOS 15 in 2021, Apple for the first time officially supports multi-line UIButtons via the UIButton.Configuration API.

UIButton.Configuration

A configuration that specifies the appearance and behavior of a button and its contents.

UIButton.Configuration

This new API is explored in What's new in UIKit as well as the session:

Meet the UIKit button system

Every app uses Buttons. With iOS 15, you can adopt updated styles to create gorgeous buttons that fit effortlessly into your interface. We'll explore features that make it easier to create different types of buttons, learn how to provide richer interactions, and discover how you can get great buttons when using Mac Catalyst.

https://developer.apple.com/videos/play/wwdc2021/10064/

pkamb
  • 33,281
  • 23
  • 160
  • 191
0
self.btnError.titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping

self.btnError.titleLabel?.textAlignment = .center

self.btnError.setTitle("Title", for: .normal)
Bugs
  • 4,491
  • 9
  • 32
  • 41
Urvish Modi
  • 1,118
  • 10
  • 11
0

I incorporated jessecurry's answer within STAButton which is part of my STAControls open source library. I currently use it within one of the apps I am developing and it works for my needs. Feel free to open issues on how to improve it or send me pull requests.

Stunner
  • 12,025
  • 12
  • 86
  • 145
0

Adding Buttons constraints and subviews. This is how i do it in my projects, lets say its much easier like this. I literally 99% of my time making everything programmatically.. Since its much easier for me. Storyboard can be really buggy sometimes [1]: https://i.stack.imgur.com/5ZSwl.png

0

My experience:

Go to "Attribut" tab.

Texting in title, press "alt+Enter" while you want to jump to next line.

And check "Word Wrap" under "Attribut --> Control" field.

picture

0

Addition to @Amir Khorsandi answer.

You probably need just add uibutton.TitleLabel.TranslatesAutoresizingMaskIntoConstraints = false; as you added it to uibutton itself. (Sorry for c#, I think you can translate it easily). So uibutton.LineBreakMode = UILineBreakMode.WordWrap; will enable multiline text. Because that constraints will bring you problems when you will want to add image or something else in your button.

desertnaut
  • 57,590
  • 26
  • 140
  • 166
Nikita
  • 26
  • 2
0

Based on @Amir Khorsandi answer - but in case you have a button with some image:

extension UIButton {

    func enableMultiline() {
        setContentHuggingPriority(.defaultLow, for: .vertical) // high hugging priority could squeeze the button to 1 line
        titleLabel?.numberOfLines = 0
        titleLabel?.setContentCompressionResistancePriority(.required, for: .vertical)
        titleLabel?.topAnchor.constraint(greaterThanOrEqualTo: topAnchor).isActive = true
        titleLabel?.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor).isActive = true
        imageView?.setContentCompressionResistancePriority(.required, for: .horizontal) // otherwise image could be squeezed
    }

}
OrbitalMan
  • 51
  • 6
-4

Although it's okay to add a subview to a control, there's no guarantee it'll actually work, because the control might not expect it to be there and might thus behave poorly. If you can get away with it, just add the label as a sibling view of the button and set its frame so that it overlaps the button; as long as it's set to appear on top of the button, nothing the button can do will obscure it.

In other words:

[button.superview addSubview:myLabel];
myLabel.center = button.center;
Becca Royal-Gordon
  • 17,541
  • 7
  • 56
  • 91
  • 2
    Please, if you suggest this kind of ugly approach at least don't make mistakes in your code example :). The label shouldn't have the same center as button if it's a subview. Use myLabel.frame = button.bounds; or myLabel.center = cgPointMake(button.frame.size.with*0.5,button.frame.size.height*0.5) – JakubKnejzlik Aug 06 '14 at 14:53
  • 2
    @GrizzlyNetch I'm specifically advising *not* to add it as a subview of the button. `addSubview:` here adds it to `button`'s superview, thus making it a sibling of the button, so matching their centers is correct. – Becca Royal-Gordon Aug 08 '14 at 06:49
  • Ah, my mistake! You are right, I've missed the "small" fact it's in the superview :D ... sorry :) – JakubKnejzlik Aug 08 '14 at 08:16
  • That it is not exactly what is asked, this solution would be a bad way to implement the question since the element button has a title. – Pablo Blanco Jul 17 '19 at 07:21
  • 1
    This was reasonable advice in iPhone OS 2.0. There are much better ways to do it by now. :^) – Becca Royal-Gordon Jul 23 '19 at 05:58