With the new iOS7 UIView tint color it becomes pretty easy to theme an entire app quickly. It even changes the color of the text caret when editing UITextFields.
However, the keyboard's bottom right 'dismiss' button (can be Done, Search, etc) is always blue. Is there any way to change this? It would look really nice if it matched the tint color of the rest of the app.
With a little hack maybe you can achieve the effect you are looking for. But it might not be able to pass the app review.
-(NSArray*)subviewsOfView:(UIView*)view withType:(NSString*)type{
NSString *prefix = [NSString stringWithFormat:#"<%#",type];
NSMutableArray *subviewArray = [NSMutableArray array];
for (UIView *subview in view.subviews) {
NSArray *tempArray = [self subviewsOfView:subview withType:type];
for (UIView *view in tempArray) {
[subviewArray addObject:view];
}
}
if ([[view description]hasPrefix:prefix]) {
[subviewArray addObject:view];
}
return [NSArray arrayWithArray:subviewArray];
}
-(void)addColorToUIKeyboardButton{
for (UIWindow *keyboardWindow in [[UIApplication sharedApplication] windows]) {
for (UIView *keyboard in [keyboardWindow subviews]) {
for (UIView *view in [self subviewsOfView:keyboard withType:#"UIKBKeyplaneView"]) {
UIView *newView = [[UIView alloc] initWithFrame:[(UIView *)[[self subviewsOfView:keyboard withType:#"UIKBKeyView"] lastObject] frame]];
newView.frame = CGRectMake(newView.frame.origin.x + 2, newView.frame.origin.y + 1, newView.frame.size.width - 4, newView.frame.size.height -3);
[newView setBackgroundColor:[UIColor greenColor]];
newView.layer.cornerRadius = 4;
[view insertSubview:newView belowSubview:((UIView *)[[self subviewsOfView:keyboard withType:#"UIKBKeyView"] lastObject])];
}
}
}
}
The app I used to decode the view hierarchy was : http://revealapp.com/
The end result is like this:
You can not change button tint color but you can set keyboard Tint color by using UIKeyboardAppearance
Example: yourTextField.keyboardAppearance = UIKeyboardAppearanceDark;
Here is a very nice document provided by Apple, take a look here:
Managing the Keyboard
let colors: [UIColor] = [.red, .blue, .green, .purple, .yellow, .orange, .brown]
if let window = UIApplication.shared.windows.first(where: {
$0.isType(string: "UIRemoteKeyboardWindow")
}) {
if let keyplaneView = window.subview(ofType: "UIKBKeyplaneView") {
for (i, keyView) in keyplaneView.subviews.filter({
$0.isType(string: "UIKBKeyView")
}).enumerated() {
let view = UIView(frame: keyView.bounds)
view.backgroundColor = colors[i].withAlphaComponent(0.5)
keyView.addSubview(view)
}
}
}
Here is a color map of the keys in the UIKBKeyplaneView:
I have a UISearchBar that has a cancel button (it's displayed using -(void)setShowsCancelButton:animated). I've changed the tintColor of the search bar like this in an attempt to get a grayish searchbar:
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 0, 320, 40)];
searchBar.tintColor = [UIColor colorWithWhite:0.8 alpha:1.0];
This is what it looks like now - notice how the cancel button is also gray: http://twitpic.com/c0hte
Is there a way to set the color of the cancel button separately so it looks more like this: http://twitpic.com/c0i6q
You can use UIAppearance to style the cancel button without iterating subviews of the UISearchBar, but the UIButton header does not currently have any methods annotated with UI_APPEARANCE_SELECTOR.
EDIT: Drill down the subviews till you get that cancel button
But this usually returns nil until
searchBar.setShowsCancelButton(true, animated: true) is called.
extension UISearchBar {
var cancelButton : UIButton? {
if let view = self.subviews.first {
for subView in view.subviews {
if let cancelButton = subView as? UIButton {
return cancelButton
}
}
}
return nil
}
}
In iOS 5.0+, you can use the appearnce proxy.
Before the search bar is showed.:
UIBarButtonItem *searchBarButton = [UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil];
[searchBarButton setBackgroundImage:myCancelButtonImageNormal forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[searchBarButton setBackgroundImage:myCancelButtonImageHighlighted forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[searchBarButton setTitleTextAttributes:barButtonTitleTextAttributesNormal forState:UIControlStateNormal];
[searchBarButton setTitleTextAttributes:barButtonTitleTextAttributesHighlighted forState:UIControlStateHighlighted];
If you use [UIButton appearanceWhenContainedIn:[UISearchBar class], nil], it will affect other buttons (e.g. clear button). So, you'd better not use UIButton's appearnce. Try UIBarButtonItem.
Change the title of 'Cancel' button:
[[UIButton appearanceWhenContainedIn:[UISearchBar class], nil] setTitle:#"newTitle" forState:UIControlStateNormal];
Swift equivalent:
let cancelButton = UIButton.appearance(whenContainedInInstancesOf: [UISearchBar.self])
cancelButton?.setTitle("cancel".localized, for: .normal)
Though this might not be exactly relevant to the original question, the solution is still applicable in the larger sense of trying to customize the Cancel button in the UISearchBar. Thought this will help others who are stuck in such a scenario.
My situation was to change the cancel button's title, but with a twist, wherein I did not want to show the cancel button by default but only wanted it to show up, when the user enters the search mode (by clicking inside the search text field). At this instant, I wanted the cancel button to carry the caption "Done" ("Cancel" was giving a different meaning to my screen, hence the customization).
Nevertheless, here's what I did (a combination of caelavel's and Arenim's solutions):
Subclassed UISearchBar as MyUISearchBar with these two methods:
-(void) setCloseButtonTitle: (NSString *) title forState: (UIControlState)state
{
[self setTitle: title forState: state forView:self];
}
-(void) setTitle: (NSString *) title forState: (UIControlState)state forView: (UIView *)view
{
UIButton *cancelButton = nil;
for(UIView *subView in view.subviews){
if([subView isKindOfClass:UIButton.class])
{
cancelButton = (UIButton*)subView;
}
else
{
[self setTitle:title forState:state forView:subView];
}
}
if (cancelButton)
[cancelButton setTitle:title forState:state];
}
And in the viewcontroller which uses this Searchbar, the following piece of code takes care of showing the cancel button and customizing its title:
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
MyUISearchBar *sBar = (MyUISearchBar *)searchBar;
[sBar setShowsCancelButton:YES];
[sBar setCloseButtonTitle:#"Done" forState:UIControlStateNormal];
}
Strangely enough, I did not have to do anything to hide the cancel button, as it is hidden by default, when the search mode is exited.
What you want to do is pretty tough. There is no built-in hook to get at the cancel button.
However, there are a couple of options if you are willing to jimmy open the hood.
First off, UISearchBar is a UIView, and the Cancel button is also a view, which is added into the search bar as a subview, just as you would expect.
I have experimented a little, and can tell you that when the button is onscreen it has a size of 48,30.
So in viewWillAppear, you can do something like this:
Find the cancel button view in [searchBar subviews] by looking for one with size 48,30. (There only seems to be one -- this could change...) You could be doubly careful and look for one that is in approximately the correct position (differs in landscape and portrait).
Add a subview to the cancel button.
The subview should be a UIControl (so that you can set enabled = NO, in order to make sure touch events get to the actual cancel button)
It needs to have the right color and rounded corners; you will need to fudge the size for reasons I don't yet understand (55,30 seems to work)
This will work if searchBar.showsCancelButton is always YES; if you want it to disappear when not editing the search string, you will need to find a hook to add the overlay each time the cancel button appears.
As you can see, this is some ugly tinkering. Do it with eyes wide open.
You can find the cancel button by looping through the subviews of the search bar and checking for the class type (instead of the size):
UIButton *cancelButton = nil;
for(UIView *subView in yourSearchBar.subviews){
if([subView isKindOfClass:UIButton.class]){
cancelButton = (UIButton*)subView;
}
}
And then change the tint color:
[cancelButton setTintColor:[UIColor colorWithRed:145.0/255.0 green:159.0/255.0 blue:179.0/255.0 alpha:1.0]];
If you want to configure your cancel button on UISearchBar you should get the UIButton object from your UISearchBar object. Example below
UISearchBar *s_bar = [[UISearchBar alloc] initWithFrame:CGRectMake(50,20,300,30)];
s_bar.delegate = self;
s_bar.barStyle = UIBarStyleDefault;
s_bar.showsCancelButton = YES;
UIButton *cancelButton;
for (id button in s_bar.subviews)
{
if ([button isKindOfClass:[UIButton class]])
{
cancelButton=(UIButton*)button;
break;
}
}
Custom UISearchBar and override method -addSubview:
- (void) addSubview:(UIView *)view {
[super addSubview:view];
if ([view isKindOfClass:UIButton.class]) {
UIButton *cancelButton = (UIButton *)view;
[cancelButton setBackgroundImage:[UIImage imageNamed:#"xxxx.png"] forState:UIControlStateNormal];
[cancelButton setBackgroundImage:[UIImage imageNamed:#"yyyy.png"] forState:UIControlStateHighlighted];
}
}
I'll give a detailed answered regarding the UIAppearance technique. First, you need to understand that the cancel button is a private UINavigationButton:UIButton. After some inspection, it appears that UINavigationButton will respond to those UIAppearance selectors:
// inherited from UINavigationButton
#selector(setTintColor:)
#selector(setBackgroundImage:forState:style:barMetrics:)
#selector(setBackgroundImage:forState:barMetrics:)
#selector(setTitleTextAttributes:forState:)
#selector(setBackgroundVerticalPositionAdjustment:forBarMetrics:)
#selector(setTitlePositionAdjustment:forBarMetrics:)
#selector(setBackButtonBackgroundImage:forState:barMetrics:)
#selector(setBackButtonTitlePositionAdjustment:forBarMetrics:)
#selector(setBackButtonBackgroundVerticalPositionAdjustment:forBarMetrics:)
// inherited from UIButton
#selector(setTitle:forState:)
Coincidentally, those selectors match those of a UIBarButtonItem. Meaning the trick is to use two separate UIAppearance to handle the private class UINavigationButton.
/* dual appearance technique by Cœur to customize a UINavigationButton */
Class barClass = [UISearchBar self];
UIBarButtonItem<UIAppearance> *barButtonItemAppearanceInBar = [UIBarButtonItem appearanceWhenContainedIn:barClass, nil];
[barButtonItemAppearanceInBar setTintColor:...];
[barButtonItemAppearanceInBar setBackgroundImage:... forState:... style:... barMetrics:...];
[barButtonItemAppearanceInBar setBackgroundImage:... forState:... barMetrics:...];
[barButtonItemAppearanceInBar setTitleTextAttributes:... forState:...];
[barButtonItemAppearanceInBar setBackgroundVerticalPositionAdjustment:... forBarMetrics:...];
[barButtonItemAppearanceInBar setTitlePositionAdjustment:... forBarMetrics:...];
// only for a backButton in an UINavigationBar, not for a cancelButton in an UISearchBar
//[barButtonItemAppearanceInBar setBackButtonBackgroundImage:... forState:... barMetrics:...];
//[barButtonItemAppearanceInBar setBackButtonTitlePositionAdjustment:... forBarMetrics:...];
//[barButtonItemAppearanceInBar setBackButtonBackgroundVerticalPositionAdjustment:... forBarMetrics:...];
UIButton<UIAppearance> *buttonAppearanceInBar = [UIButton appearanceWhenContainedIn:barClass, nil];
// warning: doesn't work for iOS7+
[buttonAppearanceInBar setTitle:... forState:...];
This will let you customize your Cancel button as much as you want.
After you've initialized your UISearchBar, you can probe into it's subviews and customize each of them. Example:
for (UIView *view in searchBar.subviews) {
//if subview is the button
if ([[view.class description] isEqualToString:#"UINavigationButton"]) {
//change the button images and text for different states
[((UIButton *)view) setEnabled:YES];
[((UIButton *)view) setTitle:nil forState:UIControlStateNormal];
[((UIButton *)view) setImage:[UIImage imageNamed:#"button image"] forState:UIControlStateNormal];
[((UIButton *)view) setBackgroundImage:[UIImage imageNamed:#"button"] forState:UIControlStateNormal];
[((UIButton *)view) setBackgroundImage:[UIImage imageNamed:#"button_pressed"] forState:UIControlStateSelected];
[((UIButton *)view) setBackgroundImage:[UIImage imageNamed:#"button_pressed"] forState:UIControlStateHighlighted];
//if the subview is the background
}else if([[view.class description] isEqualToString:#"UISearchBarBackground"]) {
//put a custom gradient overtop the background
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[[some uicolor] CGColor], (id)[[another uicolor] CGColor], nil];
[view.layer insertSublayer:gradient atIndex:0];
//if the subview is the textfield
}else if([[view.class description] isEqualToString:#"UISearchBarTextField"]){
//change the text field if you wish
}
}
Worked out great for me! Especially the gradient :)
Swift 2.1.1:
There's no simple way to hook in and style the search bar, you need to grab the subview manually from the search bar and then apply your changes.
var cancelButton: UIButton
let topView: UIView = self.customSearchController.customSearchBar.subviews[0] as UIView
for subView in topView.subviews {
if subView.isKindOfClass(NSClassFromString("UINavigationButton")!) {
cancelButton = subView as! UIButton
cancelButton.enabled = true
cancelButton.setTitle("TestTitle", forState: UIControlState.Normal) // Change to set the title
cancelButton.setBackgroundImage(UIImage(named: "ImageName"), forState: .Normal) // Change this to set a custom cancel button image, set the title to "" to remove 'Cancel' text
}
}
First of all I'd like to thank #Eliott from this https://stackoverflow.com/a/37381821/1473144
I had to make a few adjustments for his answer to work in my specs that go below.
Please, I ask the OP to update the accepted answer as it's VERY outdated.
Swift 3, iOS 10 & Xcode 8.2.1
searchBar.showsCancelButton = true
var cancelButton: UIButton
let topView: UIView = self.searchBar.subviews[0] as UIView
for subView in topView.subviews {
if let pvtClass = NSClassFromString("UINavigationButton") {
if subView.isKind(of: pvtClass) {
cancelButton = subView as! UIButton
cancelButton.setTitle("", for: .normal)
cancelButton.tintColor = UIColor.black
cancelButton.setImage(#imageLiteral(resourceName: "searchX"), for: .normal)
}
}
}
Well, here is function, which can change Cancel's button label. Modify it, if you want.
Usage is:
nStaticReplaceStringInView(mySearchBar, #"Cancel", #"NewCancelButtonLabel");
void nStaticReplaceStringInView(UIView * view, NSString * haystack, NSString * needle)
{
for(int i=0; i<[view.subviews count]; i++)
{
nStaticReplaceStringInView([view.subviews objectAtIndex:i], haystack,needle);
}
if([view respondsToSelector:#selector(titleForState:)])
{
//NSLog(#"%# || %#",[view titleForState:UIControlStateNormal], haystack);
if(NSStrEq([view titleForState:UIControlStateNormal] , haystack))
{
[view setTitle: needle forState: UIControlStateNormal];
}
}
}
- (void) searchBarTextDidBeginEditing:(UISearchBar *)theSearchBar
{
NSArray *arr = [theSearchBar subviews];
UIButton *cancelButton = [arr objectAtIndex:3];
[cancelButton setTitle:#"yourtitle" forState:UIControlStateNormal];
}
Just take a log of arr amd see at which index control lies. In the same way u can set UITextField properties:
NSArray *arr = [searchbar subviews];
UITextField *searchfield = [arr objectAtIndex:2];
[searchfield setTextAlignment:UITextAlignmentRight];
I have many UISearchBar items throughout my app, so I wrote this category to add a property so you can access mySearchBar.cancelButton. (If you're new to categories, read more about extending objects with Categories here.)
Keep in mind you should only access this when the Cancel button is visible because UISearchBar seems to create a new button object every time it shows. Don't save the pointer to the cancelButton, just get it when needed:
#interface UISearchBar (cancelButton)
#property (readonly) UIButton* cancelButton;
- (UIButton *) cancelButton;
#end
#implementation UISearchBar (cancelButton)
- (UIButton *) cancelButton {
for (UIView *subView in self.subviews) {
//Find the button
if([subView isKindOfClass:[UIButton class]])
{
return (UIButton *)subView;
}
}
NSLog(#"Error: no cancel button found on %#", self);
return nil;
}
#end
stupid way
for(id cc in [SearchBar subviews])
{
if([cc isKindOfClass:[UIButton class]])
{
UIButton *btn = (UIButton *)cc;
......
Do whatever you want
.......
}
}
extension UISearchBar {
var cancelButton : UIButton? {
let topView: UIView = self.subviews[0] as UIView
if let pvtClass = NSClassFromString("UINavigationButton") {
for v in topView.subviews {
if v.isKind(of: pvtClass) {
return v as? UIButton
}
}
}
return nil
}
}
UISearchBar *searchBar;
[searchBar setShowsCancelButton:YES animated:YES];
UIButton *cancelButton =
YES == [searchBar respondsToSelector:NSSelectorFromString(#"cancelButton")] ?
[searchBar valueForKeyPath:#"_cancelButton"] : nil;
cancelButton.titleEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 10);
[cancelButton setTitle:#"New :)" forState:UIControlStateNormal];
For iOS 11 and Swift 4.
Create a subclass of UISearchController.
Override method:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print("layout")
if let btn = searchBar.subviews[0].subviews[2] as? UIButton {
btn.frame = CGRect(x: 306, y: 20, width: 53, height: 30)
}
}
For iOS 10 & above, use following method
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:#[[UISearchBar class]]] setTintColor:[UIColor blackColor]];
The best way to style the cancelButton is without using UIAppearance is like this it is for Swift5 iOS13 and it works best with UISearchResultController.searchBar too
extension UISearchBar {
func changeSearchBarAppearance(appearance: MyAppearance) {
self.barTintColor = appearance.searchbar.barTintColor
self.tintColor = appearance.searchbar.tintColor
if let textField = self.subviews.first?.subviews.last?.subviews.first {
textField.tintColor = .black
}
}
}
setting serachBar tintColor will set the tintColor of all items including the cancelButton but with this the blinker in the searchField will also be set with the same tintColor so find the textfield and set its tintColor will solve the blinker issue
Does anybody know of a way to customize the appearance of the string based UISegmentedControl? I am trying to set the background color of the cell and the text color differently depending on the selected state of the item.
Alternatively, do you know of a way to create UIImages on the fly in which to include custom strings? (e.g. create UUImage with white background, overlay text, add to segmented control).
I know that you can only have strings or images in the segmented control...
UISegmentedControl has a tintColor property -- this allows you to change what color the control is, but not the general "style" (the rounded, beveled shape):
segmentedControl.tintColor = [UIColor blueColor];
As for creating UIImages on the fly, you can create a CGContext, do whatever drawing you need to in that context (including strings), and then get a UIImage out of the context's CGImage:
CGContextRef drawContext = CGBitmapContextCreate(<many parameters>);
//do drawing here
CGImageRef finalImage = CGBitmapContextCreateImage(drawContext);
UIImage *cellImage = [UIImage finalImage];
Please note, that if you use code like UIView.appearance().tintColor = .myColor (or equiv. in ObjC), the effect most likely won't take place.
segmentedControl.tintColor = [UIColor colorWithRed:0.61176f green:0.61176f blue:0.61176f alpha:1.0f];
segmentedControl.segmentedControlStyle = UISegmentedControlStyleBar;
Most of the answers here don't answer the specific question of how to set a button color based on selected state which implies another color is desired for unselected state. I struggled with this for quite some time and wanted to share my solution for others to use.
My example uses a UISegmentedControl with three segments. The unselected color for all three should be the same to give it a uniform look. The selected state for the first and last segment have unique colors.
The issue is that the segmented control is not guaranteed to be in the same order so the colors will get mixed up as you select back and forth. Dan posted a solution that uses tags but unfortunately it's no longer guaranteed to work for iOS 6 and up.
Most of this code is taken from this post. I changed it slightly to have unique selected colors.
What makes it work is the sorting but take note of these 2 important lines for setting the selected color:
NSInteger selectedIdx = betterSegmentedControl.selectedSegmentIndex;
[[sortedViews objectAtIndex:selectedIdx] setTintColor:[self.segmentColors objectAtIndex:selectedIdx]];
- (void) updateSegmentColors
{
UIColor *checkColor = [UIColor colorWithRed: 29/255.0 green:166/255.0 blue:47/255.0 alpha:1.0];
NSArray *segmentColors = [[NSArray alloc] initWithObjects:checkColor, [UIColor blueColor], [UIColor redColor], nil];
UISegmentedControl *betterSegmentedControl = self.StatusControl;
// Get number of segments
NSUInteger numSegments = [betterSegmentedControl.subviews count];
// Reset segment's color (non selected color)
for( int i = 0; i < numSegments; i++ ) {
// reset color
[[betterSegmentedControl.subviews objectAtIndex:i] setTintColor:nil];
[[betterSegmentedControl.subviews objectAtIndex:i] setTintColor:[UIColor blueColor]];
}
// Sort segments from left to right
NSArray *sortedViews = [betterSegmentedControl.subviews sortedArrayUsingFunction:compareViewsByOrigin context:NULL];
// Change color of selected segment
NSInteger selectedIdx = betterSegmentedControl.selectedSegmentIndex;
[[sortedViews objectAtIndex:selectedIdx] setTintColor:[self.segmentColors objectAtIndex:selectedIdx]];
// Remove all original segments from the control
for (id view in betterSegmentedControl.subviews) {
[view removeFromSuperview];
}
// Append sorted and colored segments to the control
for (id view in sortedViews) {
[betterSegmentedControl addSubview:view];
}
}
NSInteger static compareViewsByOrigin(id sp1, id sp2, void *context)
{
// UISegmentedControl segments use UISegment objects (private API). But we can safely cast them to UIView objects.
float v1 = ((UIView *)sp1).frame.origin.x;
float v2 = ((UIView *)sp2).frame.origin.x;
if (v1 < v2)
return NSOrderedAscending;
else if (v1 > v2)
return NSOrderedDescending;
else
return NSOrderedSame;
}
I placed the code in it's own method because I'm loading these segmented controls in a table view and need to run it upon loading (existing states from storage) and when a user changes a selection. Now I just need to call [Self updateSegmentColors]; when something changes.
All you have to do is:
// Get an array of the subviews of a UISegmentedControl, for example myUISegmentedControl:
NSArray *arri = [myUISegmentedControl subviews];
// Change the tintColor of each subview within the array:
[[arri objectAtIndex:0] setTintColor:[UIColor redColor]];
[[arri objectAtIndex:1] setTintColor:[UIColor greenColor]];
The best way I have found of doing something like this is setting different attributes for different UIControlStates on the segmented control.
self.segmentedControl.tintColor = [UIColor cb_Grey1Color];
self.segmentedControl.backgroundColor = [UIColor cb_Grey3Color];
NSDictionary *selectedAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont cbGothamBookFontWithSize:13.0], NSFontAttributeName,
[UIColor whiteColor], NSForegroundColorAttributeName,
[UIColor cb_Grey1Color], NSBackgroundColorAttributeName, nil];
[self.segmentedControl setTitleTextAttributes:selectedAttributes forState:UIControlStateSelected];
NSDictionary *unselectedAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
[UIFont cbGothamBookFontWithSize:13.0], NSFontAttributeName,
[UIColor cb_Grey2Color], NSForegroundColorAttributeName,
[UIColor cb_Grey3Color], NSBackgroundColorAttributeName, nil];
[self.segmentedControl setTitleTextAttributes:unselectedAttributes forState:UIControlStateNormal];
Font Color swift 3 and swift 4 if you want to change
For Unselected item
segcntrl.setTitleTextAttributes(titleTextAttributes, for: .normal)
For Selected item
segcntrl.setTitleTextAttributes(titleTextAttributes, for: .selected)
//MARK:- Segment color change
self.segc.setTitleTextAttributes([NSAttributedStringKey.foregroundColor:
UIColor.white], for: UIControlState.selected)
self.segc.setTitleTextAttributes([NSAttributedStringKey.foregroundColor:
UIColor.white], for: UIControlState.normal)
As of iOS13, you would be no longer able to modify the tint color of the segment controller. Need to use selectedSegmentTintColor if the color has to be customized.
self.yourSegmentControl.selectedSegmentTintColor = UIColor(red: 240.0/255.0, green: 183.0/255.0, blue: 0.0/255.0, alpha: 1.0)
Here is a sample code that works with iOS9, but it is a hack, and might not work in later versions:
UISegmentedControl *segmentedControl = [[UISegmentedControl alloc] initWithItems:#[#"Title1", #"Title2"]];
for (id segment in [segmentedControl subviews])
{
for (id view in [segment subviews])
{
NSString *desc = [view description];
if ([desc containsString:#"UISegmentLabel"])
{
[segment setTintColor:([desc containsString:#"Title1"] ? [UIColor blueColor] : [UIColor greenColor])];
}
}
}
I was able to do it via Interface Builder in XCode 6. Attached is the tint property:
I wanted to accomplish something similar - set the background color of the selected segment to one color while the 'outline' of the rest of the segments were a different color.
Borrowing heavily from Portland Runner's answer, the idea is to subclass the UISegmentedControl, and override 2 methods to both style the initial state as well as capture the change event to style it automatically as the user selects different segments.
- (void)layoutSubviews {
[super layoutSubviews];
[self updateSegmentColors];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
[self updateSegmentColors];
}
- (void)updateSegmentColors {
NSArray* segments = [self.subviews sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
// UISegmentedControl segments use UISegment objects (private API). But we can safely cast them to UIView objects.
float v1 = ((UIView *)obj1).frame.origin.x;
float v2 = ((UIView *)obj2).frame.origin.x;
if (v1 < v2) return NSOrderedAscending;
else if (v1 > v2) return NSOrderedDescending;
else return NSOrderedSame;
}];
for (int i=0; i<segments.count; i++) {
if (i == self.selectedSegmentIndex) {
[segments[i] setTintColor:[UIColor redColor]];
} else {
[segments[i] setTintColor:[UIColor grayColor]];
}
}
}
Swift 5.0
if #available(iOS 13.0, *) {
// For selected segment
control.selectedSegmentTintColor = UIColor.red
}
// For control's background
control.layer.backgroundColor = UIColor.black.cgColor
Storyboard Solution
Xcode 11
Select the control and multiple color options are now available. If you need further refinement - check out User Defined Runtime Attributes.