32

How can I make the text in one of the buttons in my UISegmentedControl span multiple lines?

Jason
  • 14,517
  • 25
  • 92
  • 153

7 Answers7

43

Use UIAppearance to get things done. The below code snippet will work. Call this before creating your segment.

Objective-C

[[UILabel appearanceWhenContainedIn:[UISegmentedControl class], nil] setNumberOfLines:0];

Swift

UILabel.appearanceWhenContainedInInstancesOfClasses([UISegmentedControl.self]).numberOfLines = 0
buczek
  • 2,011
  • 7
  • 29
  • 40
  • 2
    Since someone downvoted: It shouldn't work (numberOfLines isn't even a UIAppearance selector), but it actually works. Proof: http://i.stack.imgur.com/gE7Dv.png Problem seems to be that the label width seems to be taken solely from the first line. That's why I added these spaces in the code. – Matthias Bauch May 23 '15 at 09:31
  • @MatthiasBauch try removing the new line character – Saranya Sivanandham May 23 '15 at 09:34
  • Without the newline it wouldn't be a multi line label. ;) Thanks anyway, but I don't have that problem anymore, I solved it with the top-voted answer a while ago. Just wanted to explain that the downvote was not necessary. – Matthias Bauch May 23 '15 at 09:36
  • This API is available on iOS 9.0+. – Yaroslav Feb 03 '16 at 14:21
  • [[UILabel appearanceWhenContainedInInstancesOfClasses:@[[UISegmentedControl class]]] setNumberOfLines:0]; this code will work for iOS 12 – R. Mohan Jan 20 '19 at 10:07
  • Swift 5 (add in AppDelegate to get this to work for controls added in Storyboard): UILabel.appearance(whenContainedInInstancesOf: [UISegmentedControl.self]).numberOfLines = 0 – D. Pratt May 20 '19 at 15:46
32

I did it this way:

  • create a multiline UILabel
  • fill the label with N lines of text
  • convert the label into an UIImage
  • set the image as a segments content

This works smooth on iOS 4, 5, 6

Sample Image iOS 5

and iOS 7 (just remove the text shadow)

Sample Image iOS 7

MultiLineSegmentedControl - header file

//
//  MultiLineSegmentedControl.h
//
//  Created by Jens Kreiensiek on 20.07.11.
//  Copyright 2011 SoButz. All rights reserved.
//
#import <Foundation/Foundation.h>

@interface MultiLineSegmentedControl : UISegmentedControl
- (void)setMultilineTitle:(NSString *)title forSegmentAtIndex:(NSUInteger)segment;
@end

MultiLineSegmentedControl - implementation file

//
//  MultiLineSegmentedControl.m
//
//  Created by Jens Kreiensiek on 20.07.11.
//  Copyright 2011 SoButz. All rights reserved.
//
#import "MultiLineSegmentedControl.h"
#import "UIView+LayerShot.h"

@interface MultiLineSegmentedControl()
@property (nonatomic, retain) UILabel *theLabel;
@end

@implementation MultiLineSegmentedControl
@synthesize theLabel;

- (void)dealloc
{
    self.theLabel = nil;
    [super dealloc];
}


- (UILabel *)theLabel
{
    if (!self->theLabel) {

        self->theLabel = [[UILabel alloc] initWithFrame:CGRectZero];
        self->theLabel.textColor = [UIColor whiteColor];
        self->theLabel.backgroundColor = [UIColor clearColor];
        self->theLabel.font = [UIFont boldSystemFontOfSize:13];
        self->theLabel.textAlignment = UITextAlignmentCenter;
        self->theLabel.lineBreakMode = UILineBreakModeWordWrap;
        self->theLabel.shadowColor = [UIColor darkGrayColor];
        self->theLabel.numberOfLines = 0;
    }

    return self->theLabel;
}


- (void)setMultilineTitle:(NSString *)title forSegmentAtIndex:(NSUInteger)segment
{
    self.theLabel.text = title;
    [self.theLabel sizeToFit];

    [self setImage:self.theLabel.imageFromLayer forSegmentAtIndex:segment];
}

@end

UIView+LayerShot - header file

//
//  UIView+LayerShot.h
//
//  Created by Jens Kreiensiek on 29.06.12.
//  Copyright (c) 2012 SoButz. All rights reserved.
//
#import <UIKit/UIKit.h>

@interface UIView (LayerShot)
- (UIImage *)imageFromLayer;
@end

UIView+LayerShot - implementation file

//
//  UIView+LayerShot.m
//
//  Created by Jens Kreiensiek on 29.06.12.
//  Copyright (c) 2012 SoButz. All rights reserved.
//
#import "UIView+LayerShot.h"
#import <QuartzCore/QuartzCore.h>

@implementation UIView (LayerShot)

- (UIImage *)imageFromLayer
{
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
    [self.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

@end

Use it just like a normal UISegmentedControl:

...

MultiLineSegmentedControl *segment = [[MultiLineSegmentedControl alloc] 
    initWithItems:[NSArray arrayWithObjects:@"A", @"B", nil]];

segment.segmentedControlStyle = UISegmentedControlStyleBar;
segment.frame = CGRectMake(0, 0, 200, segment.frame.size.height * 1.5);

[segment setMultilineTitle:@"Title A\nSubtitle A" forSegmentAtIndex:0];
[segment setMultilineTitle:@"Title B\nSubtitle B" forSegmentAtIndex:1];

[self.view addSubview:segment];
[segment release];

...
Jenson
  • 909
  • 14
  • 20
  • @SoButz: I can't see any subtitle. I created subclass of UISegmentedControl & added above code. Create a MultiLineSegmentedControl in viewDidLoad of my view controller. But its not showing subtitle. I have #import "MultiLineSegmentedControl.h". Can you help me ? Thanks – iOSAppDev Dec 06 '12 at 07:50
  • iOSAppDev, I have updated the example to the method I'm using now on iOS 6. Give it a try... – Jenson Dec 12 '12 at 18:43
  • 1
    @SoButz: I have Segment names as @"All", @"My\nFriends",@"Closing\nSoon", @"Nearby". But still its not showing correctly. [Have a look](http://s8.postimage.org/hb724i105/SEGMENTED.png) Whats going wrong here ? – iOSAppDev Jan 16 '13 at 09:35
  • m also having the same problem as specified by @iOSAppDev – Harshit Gupta May 05 '14 at 20:33
16

Swift 3+ syntax based on answer by @Saranya Sivanandham

UILabel.appearance(whenContainedInInstancesOf: [UISegmentedControl.self]).numberOfLines = 0
Maverick
  • 3,209
  • 1
  • 34
  • 40
  • 3
    it is not working for me :( did anyone try this and had the expected result ?? – Niib Fouda Mar 29 '18 at 10:41
  • @Venkatesh Chejaria. Yes, adding this line in your App Delegate or any other class would work for UISegmentedControl that's added either programmatically or from Storyboard. – Maverick Jan 19 '19 at 13:21
3

The approach above is better, but for the sake of having an alternative, you can do something like:

for(UIView *subview in segmentedControl.subviews) {
        if([NSStringFromClass(subview.class) isEqualToString:@"UISegment"]) {
            for(UIView *segmentSubview in subview.subviews) {
                if([NSStringFromClass(segmentSubview.class) isEqualToString:@"UISegmentLabel"]) {
                    UILabel *label = (id)segmentSubview;
                    label.numberOfLines = 2;
                    label.text = @"Hello\nWorld";
                    CGRect frame = label.frame;
                    frame.size = label.superview.frame.size;
                    label.frame = frame;
                }
            }
        }
    }
Snowman
  • 31,411
  • 46
  • 180
  • 303
  • Hello @moby, thanks for your alternative. if I want to give a different name for my label text, how can I do it in your alternative? – benhi Dec 09 '14 at 10:07
1

Years later...

     for segment in segmented.subviews{
        for label in segment.subviews{
            if let labels = label as? UILabel{
                labels.numberOfLines = 2

            }
        }
    }
Emilio Hoffmann
  • 274
  • 2
  • 11
1

For iOS 12, the below code will work like charm

    [[UILabel appearanceWhenContainedInInstancesOfClasses:@[[UISegmentedControl class]]] setNumberOfLines:0];
R. Mohan
  • 2,182
  • 17
  • 30
0

I have a storyboard with UISegmentedControl in some views. I drag those controls to my class as @IBOutlet. Here are some codes to make labels in segmented controls multiple lines:

class MyViewController: UIViewController {
  ...
  @IBOutlet weak var segmentedCtrl: UISegmentedControl!
  {
     didSet {
       // localize strings
       let text1 = ... // localizedString
       ...
       segmentedCtrl.setTitle(text1, forSegmentAt: 0)
       ...
       // Following code making multiple lines happen!
       UILabel.appearance(whenContainedInInstancesOf:     
         [UISegmentedControl.self]).numberOfLines = 0
     }
     ...
  } 
David.Chu.ca
  • 37,408
  • 63
  • 148
  • 190