Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Subclassed UISegmentedControl titleTextAttributes not always applied

Scenario

I have a peculiar bug in my subclassed UISegmentedControl. What I have simply done is subclassed a UISegmentedControl to remove the default indicators of selection/deselection and used a custom CALayer as an indicator for the same. At the same time, I have modified the titleTextAttributes to complement the UI.

Problem

When the view first appears and default selected index is 0, it looks like this:

enter image description here

When I try to change the selectedSegmentIndex to 1 it becomes:

enter image description here

Where it should look like this (corrected once you change the selectedSegmentIndex a few times):

enter image description here

This behaviour of titleTextAttributes not changing is random, however 2 cases when this happens 100% of the time are:

  • View appears, and you select segment at index 1
  • Select last segment, and then select segment 0

I have not found subclassing notes in the UISegmentedControl official docs.

Sample project HERE.

NOTE: If you use only 2 segments, you will never the see the title of the segment at index 1 on selection.

like image 452
n00bProgrammer Avatar asked Mar 17 '26 03:03

n00bProgrammer


1 Answers

Indeed it's a weird behavior.

You can workaround it adding an explicit call to [self setNeedsLayout] after changing the indication layer frame.

//
//  RBLSegmentedControl.m
//  Rabbler
//
//  Created by Utkarsh Singh on 05/11/15.
//  Copyright © 2015 Neeraj  SIngh. All rights reserved.
//

#import "NPSegmentedControl.h"

@interface NPSegmentedControl ()

@property (nonatomic, retain) CALayer* selectedIndicationLayer;

@end

@implementation NPSegmentedControl

- (instancetype)initWithItems:(NSArray *)items {

    if (self = [super initWithItems:items]) {

        self.selectedIndicationLayer.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width / items.count, self.frame.size.height);
        [self addTarget:self action:@selector(segmentedControlDidChange) forControlEvents:UIControlEventValueChanged];

        NSDictionary* selectedAttributes = @{NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue-Light" size:15],
                                             NSForegroundColorAttributeName:[UIColor whiteColor]};
        NSDictionary* deselectedAttributes = @{NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue-Light" size:15],
                                               NSForegroundColorAttributeName:[UIColor redColor]};
        NSDictionary* highlightedAttributes = @{NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue-Light" size:15],
                                                NSForegroundColorAttributeName:[[UIColor redColor] colorWithAlphaComponent:0.6f]};

        [self setTitleTextAttributes:deselectedAttributes forState:UIControlStateNormal];
        [self setTitleTextAttributes:selectedAttributes forState:UIControlStateSelected];
        [self setTitleTextAttributes:highlightedAttributes forState:UIControlStateHighlighted];
    }

    return self;
}

- (void) segmentedControlDidChange {

    [self animateSegmentSelectionChange];

    if (self.delegate && [self.delegate respondsToSelector:@selector(segmentedControlDidChangeSelectedSegment:)]) {

        [self.delegate segmentedControlDidChangeSelectedSegment:self.selectedSegmentIndex];
    }
}

- (void) animateSegmentSelectionChange {

    [UIView animateWithDuration:0.25
                          delay:0
                        options:(UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionBeginFromCurrentState)
                     animations:^{

                         CGRect frame = self.selectedIndicationLayer.frame;
                         frame.origin.x = self.selectedSegmentIndex * [UIScreen mainScreen].bounds.size.width / self.numberOfSegments;
                         self.selectedIndicationLayer.frame = frame;

                         [self setNeedsLayout]; // <--

                     } completion:nil];
}

- (CALayer *) selectedIndicationLayer {

    if (!_selectedIndicationLayer) {

        _selectedIndicationLayer = [CALayer layer];
        _selectedIndicationLayer.backgroundColor = [[UIColor redColor] CGColor];
        _selectedIndicationLayer.cornerRadius = self.frame.size.height /2;
        _selectedIndicationLayer.borderColor = [UIColor whiteColor].CGColor;
        _selectedIndicationLayer.borderWidth = 1.5f;
        [self.layer insertSublayer:_selectedIndicationLayer atIndex:0];
    }

    return _selectedIndicationLayer;
}

- (void) setSelectedSegmentIndex:(NSInteger)selectedSegmentIndex {

    [super setSelectedSegmentIndex:selectedSegmentIndex];

    [self animateSegmentSelectionChange];
}

- (void) setIndicatorColor:(UIColor *)indicatorColor {

    self.selectedIndicationLayer.backgroundColor = indicatorColor.CGColor;
}

@end
like image 153
Tomas Camin Avatar answered Mar 19 '26 20:03

Tomas Camin



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!