Fit UISegmentedControl to screen's width

#1

Hello,

When setting the UISegmentedControl I’d like to fit it to the screen’s width, but when I access the view frame or bounds the height and width are always zero so I set the width manually to 320,

-(void) addSegmentedControl
{
    NSArray *itemArray = @[@"Red", @"Green", @"Blue"];
    UISegmentedControl *sc = [[UISegmentedControl alloc] initWithItems:itemArray];
    float x,y,width, height;
    x = self.view.frame.origin.x;
    y = self.view.frame.origin.y + 380;
    width = 320;
    height = 50;
    sc.frame = CGRectMake(x, y, width, height);
    sc.backgroundColor = [UIColor whiteColor];
    [sc addTarget:self
           action:@selector(pickOne:)
 forControlEvents:UIControlEventValueChanged];
    
    [self.view addSubview:sc];

}

And this is called in 2 places: loadView

-(void) loadView{
    
    BNRHypnosisView *hv = [[BNRHypnosisView alloc] init];
    self.view = hv;
    self.backgroundView = hv;
    [self addSegmentedControl];

}

and in the action given to the buttons

-(void) pickOne:(id)sender{
    UISegmentedControl *sc = (UISegmentedControl *)sender;
    NSString *selected = [sc titleForSegmentAtIndex: [sc selectedSegmentIndex]];

    UIColor *color;

    if( [selected isEqualToString:@"Red"] ){
        color = [UIColor redColor];
    }else if ( [selected isEqualToString:@"Green"] ){
        color = [UIColor greenColor];
    }else if ( [selected isEqualToString:@"Blue"] ){
        color = [UIColor blueColor];
    }
    
    self.backgroundView.circleColor = color;

    [self addSegmentedControl];
}

but in landscape mode, I would also like to fit it to the screen’s width… how could I do it?

Since english is not my language and sometimes I explain myself a bit bad… here I leave a link to my code in case it helps
github.com/miguelsaddress/BNR_i … /HypnoNerd

Thank you for your help,
Miguel

#2

When you alloc-init a view without specifying a frame, it defaults to a frame with {0,0} size. This works because the view controller will then stretch out the view to fill the screen, but that is after loadView is called. To fix your issue, initialize the BNRHypnosisView with a non-zero frame.

- (void)loadView{
    CGRect frame = [UIScreen mainScreen].bounds;
    BNRHypnosisView *backgroundView = [[BNRHypnosisView alloc] initWithFrame:frame];
    self.view = hv;
    self.backgroundView = hv;
    [self addSegmentedControl];
}

I feel like you would only want to call -addSegmentedControl from loadView, else you’ll add multiple UISegmentedControl objects to the view controller’s view.

To answer your question on how to make the segmented control fit the view’s width in landscape, well, wait until chapter 15 and 16 when we discuss Auto Layout. Those chapters will walk you through the solution to that problem.

#3

Hi!
Thank you, i forgot the code to add the SegmentedControl while testing and trying :slight_smile:
I will wait then to do the adjustments to the size until the chapters you mention. Anyway, in case it helps to somebody else who may read this, I found out this

    CGRect screenRect = [[UIScreen mainScreen] bounds];    
    BNRHypnosisView *hv = [[BNRHypnosisView alloc] initWithFrame:screenRect];

which takes de bounds of the screen itself, so i use it to initialise the view and also to adjust it on rotation

Here is my controllers code in case it helps anybody

#import "BNRHypnosisViewController.h"
#import "BNRHypnosisView.h"

@interface BNRHypnosisViewController()
@property(nonatomic, weak) BNRHypnosisView *backgroundView;
@property(nonatomic, weak) UISegmentedControl *segmentedControl;
@end

@implementation BNRHypnosisViewController

-(instancetype) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if(self){
        self.tabBarItem.title = @"Hypnotize me";
        UIImage *i = [UIImage imageNamed:@"Hypno.png"];
        self.tabBarItem.image = i;
    }
    return self;
}

-(void) loadView{
    
    CGRect screenRect = [[UIScreen mainScreen] bounds];
    
    BNRHypnosisView *hv = [[BNRHypnosisView alloc] initWithFrame:screenRect];
    self.view = hv;
    self.backgroundView = hv;
    [self addSegmentedControl];

}

-(void) addSegmentedControl
{
    NSArray *itemArray = @[@"Red", @"Green", @"Blue"];
    UISegmentedControl *sc = [[UISegmentedControl alloc] initWithItems:itemArray];
    self.segmentedControl = sc;
    
    [self setFrameToSegmentedControl];
    self.segmentedControl.backgroundColor = [UIColor whiteColor];
    [self.segmentedControl addTarget:self
                              action:@selector(pickOne:)
                    forControlEvents:UIControlEventValueChanged];
    
    [self.view addSubview:self.segmentedControl];

}

-(void) setFrameToSegmentedControl
{
    float x,y,width, height;
    CGRect backGroundViewFrame = self.backgroundView.frame;
    
    x = backGroundViewFrame.origin.x;
    y = backGroundViewFrame.origin.y + 30;
    width = backGroundViewFrame.size.width;
    height = 50;
    self.segmentedControl.frame = CGRectMake(x, y, width, height);
}

-(void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [self.backgroundView setNeedsDisplay];
    [self setFrameToSegmentedControl];
}

-(void) pickOne:(id)sender{
    UISegmentedControl *sc = (UISegmentedControl *)sender;
    NSString *selected = [sc titleForSegmentAtIndex: [sc selectedSegmentIndex]];

    UIColor *color;

    if( [selected isEqualToString:@"Red"] ){
        color = [UIColor redColor];
    }else if ( [selected isEqualToString:@"Green"] ){
        color = [UIColor greenColor];
    }else if ( [selected isEqualToString:@"Blue"] ){
        color = [UIColor blueColor];
    }
    
    self.backgroundView.circleColor = color;
}

@end
#4

One small question (maybe you’ll find it odd but… still):

Why this line in your pickOne implementation:

    UISegmentedControl *sc = (UISegmentedControl *)sender;

With use “sender” ?