17

I have a custom UISegmentedControl. In iOS 6 and bellow it works fine. Under iOS 7.. it looks fine until I press the control, at which time, the divider image looks weird for a split second.

Here is my code:

UIImage *segmentSelected = [[UIImage imageNamed:@"segcontrol_sel.png"]
                                resizableImageWithCapInsets:UIEdgeInsetsMake(6, 6, 6, 6)];
    UIImage *segmentUnselected = [[UIImage imageNamed:@"segcontrol_unsel.png"]
                                  resizableImageWithCapInsets:UIEdgeInsetsMake(6, 6, 6, 6)];
    UIImage *segmentSelectedUnselected =
    [UIImage imageNamed:@"segcontrol_sel_uns.png"];
    UIImage *segUnselectedSelected =
    [UIImage imageNamed:@"segcontrol_uns_sel.png"];

    [[UISegmentedControl appearance] setBackgroundImage:segmentUnselected
                                               forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
    [[UISegmentedControl appearance] setBackgroundImage:segmentSelected
                                               forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
    [[UISegmentedControl appearance] setBackgroundImage:segmentUnselected
                                               forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];

    [[UISegmentedControl appearance] setDividerImage:segUnselectedSelected
                                 forLeftSegmentState:UIControlStateNormal // | UIControlStateHighlighted
                                   rightSegmentState:UIControlStateSelected
                                          barMetrics:UIBarMetricsDefault];
    [[UISegmentedControl appearance] setDividerImage:segUnselectedSelected
                                 forLeftSegmentState:UIControlStateHighlighted
                                   rightSegmentState:UIControlStateSelected
                                          barMetrics:UIBarMetricsDefault];
    [[UISegmentedControl appearance] setDividerImage:segmentSelectedUnselected
                                 forLeftSegmentState:UIControlStateSelected
                                   rightSegmentState:UIControlStateNormal //| UIControlStateHighlighted)
                                          barMetrics:UIBarMetricsDefault];
    [[UISegmentedControl appearance] setDividerImage:segmentSelectedUnselected
                                 forLeftSegmentState:UIControlStateSelected
                                   rightSegmentState:UIControlStateHighlighted
                                          barMetrics:UIBarMetricsDefault];

    UIFont *font = [UIFont systemFontOfSize:16.0f];
    UIColor *textColor = [UIColor darkGrayColor];
    NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                font, @"NSFontAttributeName",
                                textColor, @"NSForegroundColorAttributeName",
                                nil];

    [[UISegmentedControl appearance] setTitleTextAttributes:attributes
                                                   forState:UIControlStateNormal];

Any ideas what is happening when I press the UISegmentedControl that might cause the divider to be displayed wrong? Thanks?

Shaik Riyaz
  • 11,204
  • 7
  • 53
  • 70
Fervus
  • 710
  • 7
  • 18
  • 1
    No idea what's happening here, but I'm having the same problem. It seems to me like apple are simply flipping the central image (divider image) quicker than they are flipping the outer parts (background image). If you managed to find a fix could you let me know? – simonthumper Sep 21 '13 at 20:30
  • Did you find a fix? I am having the same issue. – Imran Feb 11 '14 at 20:21

6 Answers6

7

I solved this in a way similar to what user2128193 describes, but instead of adding a target for the value changed event, I subclassed UISegmentedControl and added these two methods:

- (void)sendActionsForControlEvents:(UIControlEvents)controlEvents
{
    [super sendActionsForControlEvents:controlEvents];

    if (controlEvents & UIControlEventValueChanged) {
        [self removeAnimationsRecursivelyForView:self];
    }
}

- (void)removeAnimationsRecursivelyForView:(UIView *)view
{
    [view.layer removeAllAnimations];

    for (UIView *subview in [view subviews]) {
        [self removeAnimationsRecursivelyForView:subview];
    }
}

Obviously this is still not a perfect solution, in that it relies on the internals of UISegmentedControl, but at least it will keep your code a bit cleaner.

Niels
  • 583
  • 5
  • 20
  • For some reason this works when a segment is tapped quickly, but does *not* work after a segment has been pressed for some time. – Vadim Yelagin Jun 26 '15 at 12:49
4

TL;DR Just remove all the animations in each segment. See the Code Below

I have been able to fix this issue by removing the default animations for the backgroundImage and titles (UILabel) by calling removeAllAnimations on the segment's layer and also calling removeAllAnimations on the UILabel nested inside of each segment. I placed this code inside the IBAction method that is called when the UISegmentedControl valueChanged event is fired.

- (IBAction)tabSelected:(id)sender
{
    UISegmentedControl *segmentedControl = (UISegmentedControl *)sender;

    for (UIView *segment in [segmentedControl subviews]) {
        [segment.layer removeAllAnimations];

        for (UIView *view in [segment subviews]) {
            if ([view isKindOfClass:[UILabel class]]) {
                [view.layer removeAllAnimations];
            }
        }
    }
}

I haven't tested this on content that is a UIImage but I see no reason why it wouldn't work as long as you change this line:

[view isKindOfClass:[UILabel class]

to match for the UIImage class like so:

[view isKindOfClass:[UIImage class]
Lobsterman
  • 310
  • 2
  • 11
  • I added this to: [_segmentedControl addTarget:self action:@selector(onClick) forControlEvents:UIControlEventValueChanged]; to fix the unwanted fade in animation problem. Thanks! – Imran Feb 12 '14 at 19:05
  • I can confirm this works for UIImage perfectly as well. I put the code in the IBAction for "Value Changed". Thanks! – arinmorf May 05 '14 at 16:50
2

I solved this by overriding touchesEnded in a subclass and disabling animations for CATransaction

override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
    CATransaction.setDisableActions(true)
    super.touchesEnded(touches, withEvent: event)
}
Vadim Yelagin
  • 3,112
  • 2
  • 17
  • 20
1

I was experiencing a similar problem and figured out that there are two things going on. One of them you've already dealt with (setting up images for the highlighted/selected combos). The other is that UISegmentedControl does a color highlight when you select a new segment like UITableViewCells do when you select them. And, even though I've customized all the art and the text, it's still fading to a dark color (my text even inverts) and then it comes back. During that fade, the edges of my dividers are easily seen.

I had to turn on Slow Animations in the Simulator to see this more clearly. Unfortunately, I have not been able to find a way to disable those default animations.

Andrew
  • 3,874
  • 5
  • 39
  • 67
Wyatt
  • 11
  • 1
  • Thanks Wyatt! I still couldn't find a correct way of doing this. Your answer sheds some light though. Looks like setting a divider for the state SelectedSelected helps a bit, but still not enough. – Fervus Sep 26 '13 at 07:55
1

To add a little more detail to Wyatt's answer, what is happening is that the "divider" image and the new "selected" image are changed instantly on touchUp, but the previously-selected segment's transition from the "selected" image to the "unselected" image is performed via an animated crossfade.

If you have a wide "divider" image with high contrast between the "divider" image and the "selected" image, this leads to an annoying flash.

sumizome
  • 712
  • 8
  • 11
0

Try setting the tintcolor property of segment control. I had this issue. I set the tint colour to selection colour and no images were set for divider. Since this is IOS 7 you can do so for IOS 7 and keep divider image code for IOS 6. To make things clear I am talking tintcolor instance level property of UISegmentControl.