UISegmentedControl选中的分段颜色

73

有没有办法自定义UISegmentedControl中选定段的颜色?

我发现了segmentedController.tintColor属性,它允许我自定义整个分段控件的颜色。 问题在于,当我为tintColor属性选择明亮的颜色时,选定的段几乎无法被识别(它的颜色与其余分段控件的颜色几乎相同,因此很难区分选定和未选定的段)。 所以我不能使用任何好看的明亮颜色作为分段控制的颜色。 解决方案是一些单独的选定段颜色的属性,但我找不到这个属性。有人解决了这个问题吗?


理论上,此组件旨在防止此类情况发生。如果您选择背景颜色和色调颜色,则会为所选和未选中的内容交替显示。例如,如果您选择黑色背景和白色色调,则在选择其中一个时,它将以白色背景和黑色色调放置,反之亦然。 - jose920405
https://dev59.com/1lwZ5IYBdhLWcg3wROeN#55374590 - Lal Krishna
23个回答

74

这是改变选中段到任何RGB颜色的最简单方法。无需子类化或黑客。

segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;

UIColor *newTintColor = [UIColor colorWithRed: 251/255.0 green:175/255.0 blue:93/255.0 alpha:1.0];
    segmentedControl.tintColor = newTintColor;

UIColor *newSelectedTintColor = [UIColor colorWithRed: 0/255.0 green:175/255.0 blue:0/255.0 alpha:1.0];
[[[segmentedControl subviews] objectAtIndex:0] setTintColor:newSelectedTintColor];

这个示例展示了重要的步骤:

  1. 将控件样式设置为“StyleBar”,这是必须的才能使其工作。
  2. 首先将整个控件的未选中颜色设置为橙色。
  3. 将所选段的颜色设置为绿色。

备注:

  • 步骤1和2可以在接口构建器中完成,也可以像示例中一样在代码中完成。但步骤3只能在代码中完成。
  • 使用“123.0/255.0”这样的符号设置颜色值只是为了突出RGB值,而不是UIColor所需的标准化浮点值(如果您愿意,可以忽略它)。

为你点赞,伙计!感谢你回答这个老问题。我找到了它,并且你的回答对我有很大帮助!我已经实现了分段控制,所以我只需要使用最后一行即可。非常完美,谢谢! - Stunner
9
我认为所选择的段的索引与分段控件的子视图索引不同。 - Jack
5
这太有道理了,但我就是无法理解。我在IB中创建了一个由两个分段UISegCont组成的控件,选择了索引0,并将色调设置为深灰色。然后我使用最后一行代码定义所有选定分段的颜色。但当VC出现时,索引0的分段是黑色的,索引1的分段是深灰色的。然后当我点击索引1的分段时,索引0的分段就变成了我自定义的颜色。这是怎么回事?看起来可能需要每次 UISegCont 状态发生改变时重新定义颜色。 - mputnamtennessee
4
不起作用。将newTintColor设置为整个控件的tintColor可以工作,但将newSelectedTintColor设置为第一个子视图的tintColor并没有产生任何效果。 - Alexis
6
注意:.segmentedControlStyle 已在 iOS 7.0 中被弃用。 - taber

53

我找到了一种简单的方法,在UISegmentControl中为所选段添加颜色。

sender是UISegmentControl。

for (int i=0; i<[sender.subviews count]; i++) 
{
    if ([[sender.subviews objectAtIndex:i]isSelected] ) 
    {               
    UIColor *tintcolor=[UIColor colorWithRed:127.0/255.0 green:161.0/255.0 blue:183.0/255.0 alpha:1.0];
    [[sender.subviews objectAtIndex:i] setTintColor:tintcolor];
    }
   else 
    {
        [[sender.subviews objectAtIndex:i] setTintColor:nil];
    }
}

检查它对我是否有效


4
如果子视图响应isSelected和setTintColor选择器,则应添加健全性检查,否则当苹果更改UISegmentedControl的工作方式时,代码将崩溃。请注意,不要改变原来的意思。 - srgtuszy
4
尽管这似乎是一个折中的方法,但它并没有使用私有API。理论上来说,它应该是可以使用的。 - Tom Fobear
2
这是一个不错的解决方案,但我发现我还需要删除break,并添加else [[sender.subviews objectAtIndex:i] setTintColor:sender.tintColor];来重置旧的色调颜色。 - elimirks
我在我的应用程序中尝试了上述代码,它没有被拒绝。我认为我的目标版本是iOS 8,这就是为什么它没有被拒绝的原因。 - ShujatAli
1
应用程序在iOS 13中崩溃了。 - Hitesh Surani
显示剩余2条评论

23
为了做到这一点,你只需找到所选的片段,例如通过迭代分段控件的子视图并测试isSelected属性,然后在该子视图上简单地调用setTintColor:方法。
我通过在Interface Builder中将一个操作连接到每个分段控件的ValueChanged事件中,在视图控制器文件中连接它们到这个方法,这本质上是msprague的答案。
- (IBAction)segmentedControlValueChanged:(UISegmentedControl*)sender
{
    for (int i=0; i<[sender.subviews count]; i++)
    {
        if ([[sender.subviews objectAtIndex:i] respondsToSelector:@selector(isSelected)] && [[sender.subviews objectAtIndex:i]isSelected])
        {
            [[sender.subviews objectAtIndex:i] setTintColor:[UIColor whiteColor]];
        }
        if ([[sender.subviews objectAtIndex:i] respondsToSelector:@selector(isSelected)] && ![[sender.subviews objectAtIndex:i] isSelected])
        {
            [[sender.subviews objectAtIndex:i] setTintColor:[UIColor blackColor]];
        }
    }
}
为了确保每次用户打开视图时控件都正确显示,我还需要覆盖-(void)viewDidAppear:animated方法并按以下方式调用该方法:
-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    //Ensure the segmented controls are properly highlighted
    [self segmentedControlValueChanged:segmentedControlOne];
    [self segmentedControlValueChanged:segmentedControlTwo];
}

如果你想将分段控件设置为在选定时使用白色的色调,那么为了获得额外的加分,你还需要在选中时将文本颜色更改为黑色。可以通过以下方式实现:

//Create a dictionary to hold the new text attributes
NSMutableDictionary * textAttributes = [[NSMutableDictionary alloc] init];
//Add an entry to set the text to black
[textAttributes setObject:[UIColor blackColor] forKey:UITextAttributeTextColor];
//Set the attributes on the desired control but only for the selected state
[segmentedControlOne setTitleTextAttributes:textAttributes forState:UIControlStateSelected];

随着iOS 6的引入,第一次在viewDidAppear方法中设置所选项目的色调不起作用了,为了解决这个问题,我使用Grand Central Dispatch在过了几分之一秒后改变所选颜色:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.05 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        [self segmentedControlValueChanged:segmentedControlOne];
    });

感谢您有关iOS 6的有用评论。 我有一些代码为每个段设置色调颜色,在iOS 6中停止工作。 您的建议解决了我的问题。 - daveywc
是的,我注意到我的解决方案不适用于iOS 6。谢谢! - Mike Sprague
1
dispatch_get_current_queue() 在 iOS 6 中已被弃用,请使用 dispatch_get_main_queue()。 - tadasz
感谢@tasasz,当我注意到这一点并忘记更新答案时,我本来想更新的,现在已经更新了。根据文档,不仅它已被弃用,dispatch_get_current_queue()只应在调试期间使用,还有一个事实是UI更改应该仅在主线程上进行! - David Thompson

9
由于某些原因,苹果不允许您更改标准UISegmentedControls的颜色。
但是,有一种“合法”的方法可以解决这个问题,那就是将分段控件样式更改为UISegmentedControlStyleBar。这会使它看起来略有不同,可能不太符合您的喜好,但它确实允许颜色。
    NSArray *itemArray = [NSArray arrayWithObjects: @"One", @"Two", @"Three", nil];
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:itemArray];

//更改工具栏样式并添加以查看,然后释放分段控制器

segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
segmentedControl.tintColor = [UIColor colorWithRed:.9 green:.1 blue:.1 alpha:1]; 
[self.view addSubview:segmentedControl];
[segmentedControl release];

希望这能帮到您, Seb Kade “我在这里提供帮助”

8

编辑:此解决方案在iOS 6上不起作用。请参见下面David Thompson的答案。

这个线程真的很老,但是对我来说,没有一个简单的答案能正常工作。

只要恢复未选择分段控件的颜色,接受的答案就可以正常工作。像这样在您的值更改函数中使用:

for (int i=0; i<[control.subviews count]; i++) 
{
    if ([[control.subviews objectAtIndex:i]isSelected] ) 
    {               
        UIColor *tintcolor=[UIColor colorWithRed:127.0/255.0 green:161.0/255.0 blue:183.0/255.0 alpha:1.0];
        [[control.subviews objectAtIndex:i] setTintColor:tintcolor];
    } else {
        UIColor *tintcolor=[UIColor grayColor]; // default color
        [[control.subviews objectAtIndex:i] setTintColor:tintcolor];
    }
}

7

我知道这是一个老问题,但是现在在xcode 11+中,你可以设置选中的段落着色enter image description here

在代码中我们可以使用selectedSegmentTintColor。适用于iOS13+


1
注意:如果您的项目最初是在Xcode 10或更早版本中创建的,则可能需要将Storyboard文件的“Opens in”(位于文件检查器中)更新为Xcode 11,以便查看此功能。 - Nathan Hosselton

6
这是我修改过的uihacker CustomSegmentedControl版本(请参见评论中的信用)。我的想法是改变寻找应该更改tintColor的子视图的方式,从使用selectedIndex到isSelected方法。因为我正在使用一个自定义的UISegmentedControl,它有3个或更多段,而子视图的排序会随机更改(即使是uihacker的“hasSetSelectedIndexOnce”标志也无法解决这个问题!)。代码仍处于早期开发阶段,请自行承担风险。欢迎任何评论:)
此外,我添加了对界面生成器的支持,并覆盖了setSelectedSegmentIndex,以便它也更新颜色。享受吧!
CustomSegmentedControl.h
//
//  CustomSegmentedControl.h
//
//  Created by Hlung on 11/22/54 BE.
//  Copyright (c) 2554 __MyCompanyName__. All rights reserved.
//
//  Credit: http://uihacker.blogspot.com/2010/05/iphone-uisegmentedcontrol-custom-colors.html

@interface CustomSegmentedControl : UISegmentedControl {
    UIColor *offColor,*onColor;
}
@property (nonatomic,retain) UIColor *offColor,*onColor;
-(id)initWithItems:(NSArray *)items offColor:(UIColor*)offcolor onColor:(UIColor*)oncolor;
@end

CustomSegmentedControl.m

#import "CustomSegmentedControl.h"

@interface CustomSegmentedControl (private)
-(void)setInitialMode;
-(void)toggleHighlightColors;
@end

@implementation CustomSegmentedControl

@synthesize offColor,onColor;

-(id)initWithItems:(NSArray *)items offColor:(UIColor*)offcolor onColor:(UIColor*)oncolor {
    if (self = [super initWithItems:items]) {
        // Initialization code
        self.offColor = offcolor;
        self.onColor = oncolor;
        [self setInitialMode];

        // default to 0, other values cause arbitrary highlighting bug
        [self setSelectedSegmentIndex:0];
    }
    return self;
}
- (void)awakeFromNib {
    // default colors
    self.offColor = [UIColor colorWithWhite:0.8 alpha:1];
    self.onColor = self.tintColor;
    [self setInitialMode];

    [self setSelectedSegmentIndex:0];
}

-(void)setInitialMode
{
    // set essential properties
    [self setBackgroundColor:[UIColor clearColor]];
    [self setSegmentedControlStyle:UISegmentedControlStyleBar];

    // loop through children and set initial tint
    for( int i = 0; i < [self.subviews count]; i++ )
    {
        [[self.subviews objectAtIndex:i] setTintColor:nil];
        [[self.subviews objectAtIndex:i] setTintColor:offColor];
    }

    // listen for updates, [self setSelectedSegmentIndex:0] triggers UIControlEventValueChanged in 5.0, 4.3 doesn't (facepalm), use  if( self.window ) to fix this
    [self addTarget:self action:@selector(toggleHighlightColors) forControlEvents:UIControlEventValueChanged];
}

// ---------------
// hlung's version
// ---------------
-(void)toggleHighlightColors
{
    // the subviews array order randomly changes all the time, change to check for "isSelected" instead
    for (id v in self.subviews) {
        if ([v isSelected]) [v setTintColor:onColor];
        else [v setTintColor:offColor];
    }
}
// override: update color when set selection
- (void)setSelectedSegmentIndex:(NSInteger)selectedSegmentIndex {
    [super setSelectedSegmentIndex:selectedSegmentIndex];
    [self toggleHighlightColors];
}
// ---------------
@end

它已经工作了,但我对viewdidload方法有一个问题。我的分段控件带有默认颜色。在给任何段索引设置操作后,段的颜色会改变,但我需要一开始就这样,因为在第一次调用viewdidload时需要。这可能吗? - Anju
将以下内容添加到您的UISegmented子类中: -(void) drawRect:(CGRect)rect { [super drawRect:rect]; [self toggleHighlightColors]; } - Vassily
@Annie,@Vassily的建议解决了吗?或者你可以尝试在viewDidLoad中调用- (void)setSelectedSegmentIndex:(NSInteger)selectedSegmentIndex :) - Hlung
@DeZigny 很高兴你喜欢它 :D - Hlung

3
请使用以下内容:
[[UISegmentedControl appearance] setTitleTextAttributes:@{NSForegroundColorAttributeName : [UIColor colorWithRed:255.0/255 green:37.0/255 blue:99.0/255 alpha:1.0]} forState:UIControlStateSelected];

2
我用这个工具,一步就改变了所有的颜色。
mySegmentedControl.tintColor = [UIColor redColor]

2

为了澄清@jothikenpachi提供的答案,我们发现以下UISegmentController类别在iOS6中运行良好,并允许对段的任意开/关颜色方案进行设置。此外,如果私有方法isSelected/setTintColor:在未来的操作系统版本中更改,它将会优雅地失败。需要注意的是,涉及到私有API调用等问题。

@implementation UISegmentedControl(CustomTintExtension) {
-(void) updateCustomTintColorOn:(UIColor*)onColor Off:(UIColor*)offColor {
// Convenience function to rest the tint colors after selection, called upon change of selected index

SEL tint = @selector(setTintColor:);

for (UIView *view in [self subviews]) {
    // Loop through the views...
    if (view && ([view respondsToSelector:tint])) {
        [view performSelector:tint withObject:nil];
    }
    if (view && ([view respondsToSelector:tint])) {
        [view performSelector:tint withObject:offColor];
    }
}

// Checking if segment subview is selected...
SEL isSelected = @selector(isSelected);
for (UIView *view in [self subviews]) {
    if ([view respondsToSelector:isSelected] && [view performSelector:isSelected withObject:nil])
    {
        [view performSelector:tint withObject:onColor];
        break;
    }
}

}

请注意,这个分类方法将从UISegmentController的- (IBAction) segmentAction: (id)sender方法中调用。

还要注意,在iOS6中,似乎你需要在主UIViewController的- (void)viewDidAppear:(BOOL)animated方法中最初调用此方法,这可能会导致动画闪烁。为了最大程度地减少这种情况,可以尝试在IB中将"offColor"设置为UISegmentController的tintColor。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接