49

How to change text color of UISearchBar in iOS 7?

In iOS 6, I was subclassing the UISearchBar and in layoutSubviews customising the properties of UITextField subview of UISearchBar.

But in iOS 7, UISearchBar doesn't have UITextField as its subview. How to fix this?

Sanoj Kashyap
  • 5,020
  • 4
  • 49
  • 75
Gaurang
  • 747
  • 1
  • 7
  • 18

16 Answers16

99

In iOS 7 to access Text Field you have to reiterate on level more. Change your code like this

for (UIView *subView in self.searchBar.subviews)
{
    for (UIView *secondLevelSubview in subView.subviews){
        if ([secondLevelSubview isKindOfClass:[UITextField class]])
        {
            UITextField *searchBarTextField = (UITextField *)secondLevelSubview;

            //set font color here
            searchBarTextField.textColor = [UIColor blackColor];

            break;
        }
    }
}

Note : This is Not Public API

OR

You can use appearance Property of UIControls, Like

[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setDefaultTextAttributes:@{NSForegroundColorAttributeName:[UIColor redColor]}];

Note: Appearance proxy can be used for iOS 9.0+ OutPut

enter image description here

You can set The tintcolor to apply to key elements in the search bar.

Use tintColor to tint foreground elements.

Use barTintColor to tint the bar background.

In iOS v7.0, all subclasses of UIView derive their behavior for tintColor from the base class. See the discussion of tintColor at the UIView level for more information. Apple Doc

Dhiraj Das
  • 174
  • 9
Toseef Khilji
  • 17,192
  • 12
  • 80
  • 121
  • now after getting my textfield, if i set rightview of textfield, it is not showing it, can u suggest me how to do that?? – Mehul Thakkar Oct 07 '13 at 09:22
  • 3
    I don't think this is the right answer for iOS. See this answer instead: http://stackoverflow.com/a/19315895/1103584 – DiscDev Oct 17 '13 at 02:03
  • @spotdog13: as you suggest that will set appearance for all search bar in app, not particular one. – Toseef Khilji Oct 17 '13 at 08:43
  • 1
    I would argue that if you're theming searchbars in the same app differently, that you are not following a good UI design...the same widget reused throughout the app should look the same to avoid confusion on the part of the user, which is exactly what the Appearance proxy is for. – DiscDev Oct 17 '13 at 15:57
  • 3
    Downvoted because this is fragile and an alternative, public API is available to accomplish this. – Luke Redpath Oct 30 '13 at 17:29
  • This (the appearance proxy way, I mean) failed for one search bar in my app until I added a xib and set the views up that way instead of in code. No idea why that matters. – Tom Hamming Sep 28 '15 at 22:55
  • Using `[[UITextField appearanceWhenContainedIn:[self class], nil] setTextColor:[UIColor blueColor]];` have worked for me for one particular class only – Bhavesh Mar 11 '16 at 11:13
  • method `appearanceWhenContainedIn:` is deprecated and another method-replacement doesn't work – Vyachaslav Gerchicov Mar 17 '17 at 14:21
95

You can set the text colour by

[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setTextColor:[UIColor blueColor]];
SandeepM
  • 2,601
  • 1
  • 22
  • 32
  • 5
    But this will set appearance for all search bar in app, not particular one. – Toseef Khilji Oct 17 '13 at 08:38
  • 3
    This is what I was using under iOS6 but its not working under iOS7. If I use the `layoutSubviews` hack mentioned in the question the initial text is the correct color but it reverts once the user hits enter. Any ideas? – tapi Oct 21 '13 at 18:22
  • 5
    Upvoted because this works and uses public API. Recursing through the subview hierarchy of a framework class it's not something to be recommended - it's something to be done as a last resort and I've done it myself when I've had no option - but certainly shouldn't used when a public API is available. – Luke Redpath Oct 30 '13 at 17:27
  • You can use it in viewdidappear and set to another color in viewdiddisappear. – Cristi Băluță Nov 14 '13 at 14:21
  • 4
    Although setting 'appearance' initially looked a better/reliable solution over view-hierarchy based method but this doesn't work all the time. I have placed `UISearchBar` inside `UIView`. It affects textColor first time but subsequent removeFromSuperview/addSubview of the parent view (and some other flows as well). I'm setting this appearance in `viewWillAppear`/`viewDidAppear` methods of UIViewController. And, view-heirarchy method works like a charm (+1). – Ashok Nov 14 '13 at 19:43
  • This works and if you have multiple styles for your search bars in the app, just subclass UISearchBar and apply the each color for each subclass for `[[UITextField appearanceWhenContainedIn:[WhiteTextSearchBar class], nil] setTextColor:[UIColor whiteColor]];` – Nur Iman Izam Feb 04 '15 at 07:35
  • This is buggy when trying to customise multiple `UITextField` – Pablo A. Sep 08 '15 at 16:58
  • Using `[[UITextField appearanceWhenContainedIn:[self class], nil] setTextColor:[UIColor blueColor]];` have worked for me for one particular class only – Bhavesh Mar 11 '16 at 11:12
35

For XCode 6 (iOS8 SDK) the following DOESN'T work

[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setTextColor:[UIColor redColor]];

But the following DOES work (for deployment to iOS7 and iOS8)

[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setDefaultTextAttributes:@{NSForegroundColorAttributeName:[UIColor redColor]}];
Graham Dawson
  • 539
  • 6
  • 7
  • 9
    Many thanks. UISearchBar I swear is Apple's worst UIKit control by far. It is very hard and awkward to configure its UI and many of the methods don't actually work the way you would expect if at all. – n8tr Oct 29 '14 at 00:02
  • Confirm. This one works as expected in ```viewDidAppear/viewWillDisappear``` – SoftDesigner Dec 01 '14 at 19:20
  • 5
    For me, `textColor` only works in `viewDidAppear`, and `defaultTextAttributes` works globally. But the latter seems to change the position of the initial "Search" text a few pixels to the top. – Koraktor Feb 15 '15 at 09:43
  • 4
    Don't forget to set the font as well to avoid unpredictable UI Change: `[[UITextField appearanceWhenContainedIn: [UISearchBar class], nil] setDefaultTextAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor], NSFontAttributeName:[UIFont systemFontOfSize:14.0]}];` @Koraktor – Tim Chen Jun 01 '15 at 19:06
5

While it's true that the UIAppearance protocol is a "public API," it's not true that UITextField supports this.

If you take a look at UITextField.h and look for the string "UI_APPEARANCE_SELECTOR" you'll see that it has no instances of this string. If you look at UIButton, you find quite a few - these are all of the properties that are officially supported by the UIAppearance API. It's somewhat well-known that UITextField is not supported by the UIAppearance API, so the code in Sandeep's answer will not always work and it's actually not the best approach.

This is a useful post with useful links: http://forums.xamarin.com/discussion/175/uitextfield-appearance

The correct approach is unfortunately messy - iterate through the subviews (or subviews of main subview for iOS7) and set it manually. Otherwise you will have unreliable results. But you can just create a category for UISearchBar and add a setTextColor:(UIColor*)color method. Example:

- (void)setTextColor:(UIColor*)color
{
    for (UIView *v in self.subviews)
    {
        if([Environment isVersion7OrHigher]) //checks UIDevice#systemVersion               
        {
            for(id subview in v.subviews)
            {
                if ([subview isKindOfClass:[UITextField class]])
                {
                    ((UITextField *)subview).textColor = color;
                }
            }
        }

        else
        {
            if ([v isKindOfClass:[UITextField class]])
            {
                ((UITextField *)v).textColor = color;
            }
        }
    }
}
5

Caution : This should lead to App Rejection!

KVC FTW. This did it for me.

UITextField *searchField = [self.searchBar valueForKey:@"_searchField"];
searchField.textColor = [UIColor redColor];
Vyacheslav
  • 26,359
  • 19
  • 112
  • 194
Matthieu Riegler
  • 31,918
  • 20
  • 95
  • 134
4

You can set the text attributes like so

[[UITextField appearanceWhenContainedIn:[<YOUR_CONTROLLER_NAME> class], nil] setDefaultTextAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor], NSFontAttributeName:[UIFont systemFontOfSize:14.0]}];
mOp
  • 385
  • 3
  • 9
4

Here is working example done in C# using Xamarin:

SearchBar = new UISearchBar ();
foreach (var subView in SearchBar.Subviews) {
    foreach (var field in subView.Subviews) {
        if (field is UITextField) {
            UITextField textField = (UITextField)field;
            textField.TextColor = UIColor.White;
        }
    }
}

Hope this helps someone.

4

This seems to be the correct answer https://stackoverflow.com/a/19315895/2493073

The Swift version of it is:

UITextField.appearanceWhenContainedInInstancesOfClasses([UISearchBar.self]).textColor = UIColor.blueColor()

This would only work for iOS 9.0, in order to make it work for lower versions you'll need to follow this question. https://stackoverflow.com/a/27807417/2493073

Community
  • 1
  • 1
Fernando Mata
  • 482
  • 1
  • 10
  • 22
4

Swift Extension

public extension UISearchBar {

    public func setTextColor(color: UIColor) {
        let svs = subviews.flatMap { $0.subviews }
        guard let tf = (svs.filter { $0 is UITextField }).first as? UITextField else { return }
        tf.textColor = color
    }
}
Adam Waite
  • 19,175
  • 22
  • 126
  • 148
2

In my case, I have multiple UISearchBar objects and they need to change the textField font color. The appearanceWhenContainedIn update one UISearchBar behavior, but another doesn't.

I subclass the UISearchBar and implement custom -(id)initWithFrame: as following, and it works.

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.searchBarStyle       = UISearchBarStyleMinimal;
        self.tintColor            = [UIColor whiteColor];
        self.barTintColor         = [UIColor whiteColor];

        [[UITextField appearanceForTraitCollection:self.traitCollection whenContainedIn:[self class], nil] setDefaultTextAttributes:
         @{
           NSForegroundColorAttributeName : [UIColor whiteColor]
           }];
    }
    return self;
}

UIAppearance Protocol Reference said that,

In other words, the containment statement in appearanceWhenContainedIn: is treated as a partial ordering. Given a concrete ordering (actual subview hierarchy), UIKit selects the partial ordering that is the first unique match when reading the actual hierarchy from the window down.

So, appearanceWhenContainedIn: won't deal with all UISearchBar in the hierachy of UIWindow. And it suggests that.

Use the appearanceForTraitCollection: and appearanceForTraitCollection:whenContainedIn: methods to retrieve the proxy for a class with the specified trait collection.

AechoLiu
  • 17,522
  • 9
  • 100
  • 118
1

The easiest way to do it is by putting this code in viewDidAppear or viewWillAppear:

[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setDefaultTextAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor]}];

This works in iOS 8 and Xcode 6, unlike some of the other code. It can mess around with the font and text size, etc, but you can change that in the text attributes.

That changes the text colour for all search bars in your app. If you only want to change one, use the above code, and then in any other views with a search bar, use the same code but set the colour to whatever you want.

Jack Solomon
  • 880
  • 1
  • 8
  • 19
1

you can use search bar inside textfield

 UISearchBar *  searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 50, 320, 44) ];
searchBar.autocorrectionType = UITextAutocapitalizationTypeWords;
searchBar.delegate = self;
searchBar.searchBarStyle = UISearchBarStyleMinimal;
searchBar.barTintColor = [UIColor redColor];
[[UITextField appearanceWhenContainedIn:[searchBar class], nil]setDefaultTextAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor]}];
[self.view addSubview:searchBar];
Shanmugasundharam
  • 2,082
  • 23
  • 32
1

Update in Swift 3

UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).textColor = UIColor.black
Ben
  • 946
  • 1
  • 11
  • 19
0

Even though I would have preferred to use appearance API, it didn't work with iOS8. Here's the least hackish solution I did come with:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    if (self.shouldEnableSearchBar)
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^
        {
            UITextField *searchBarTextField = [[AFPBaseViewController findSubviewsOfView:self.searchBar ofClass:[UITextField class]] firstObject];
            searchBarTextField.textColor = AFPConstantsColorGold;
        });
    }

}

You could maybe even create a UIView category with this. The reason why this has to be called in viewDidAppear is that UISearchBar is actually contained in a ViewController, and doesn't load all its subviews until it has appeared on screen. It could be added into viewWillAppear too, but I haven't tested it.

+ (NSArray *)findSubviewsOfView:(UIView *)view ofClass:(Class)class
{
    NSMutableArray *targetSubviews = [NSMutableArray new];
    for (id subview in view.subviews)
    {
        if ([subview isKindOfClass:class])
        {
            [targetSubviews addObject:subview];
        }

        if ([subview subviews].count)
        {
            [targetSubviews addObjectsFromArray:[self findSubviewsOfView:subview ofClass:class]];
        }
    }
    return targetSubviews.copy;
}
mmackh
  • 3,550
  • 3
  • 35
  • 51
0

This is the right solution for iOS8:

[[UITextField appearanceWhenContainedIn:[UISearchBar class], nil] setDefaultTextAttributes:@{NSFontAttributeName: [UIFont fontWithName:@"Helvetica" size:14], NSForegroundColorAttributeName:[UIColor lightGrayColor]}];

You have to set the font as well, otherwise, the font size will be wrong.

Marius
  • 309
  • 1
  • 5
  • 16
0

This class will give you full control over every item in the UISearchBar


import UIKit

class SMTSearchBar: UISearchBar {

    override init(frame: CGRect) {
        super.init(frame: frame)
        initialize()
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)!
        initialize()
    }

    convenience init() {
        self.init(frame: CGRectZero)
        initialize()
    }

    // Style the view
    func initialize() {

        // Search Area
        let searchField = valueForKey("searchField") as! UITextField
        searchField.textColor = Colors.White
        searchField.font = UIFont(name: Fonts.MuseoSans500, size: 16)
        searchField.backgroundColor = Colors.Black.colorWithAlphaComponent(0.1)

        // Icons
        let searchIcon = UIImage(named: Icons.Search)?.imageWithTint(Colors.White)
        let smallClearIconNormal = UIImage(named: Icons.SmallClear)?.imageWithTint(Colors.White)
        let smallClearIconHighLight = UIImage(named: Icons.SmallClear)?.imageWithTint(Colors.White.colorWithAlphaComponent(0.5))
        setImage(searchIcon, forSearchBarIcon: .Search, state: .Normal)
        setImage(smallClearIconHighLight, forSearchBarIcon: .Clear, state: .Highlighted)
        setImage(smallClearIconNormal, forSearchBarIcon: .Clear, state: .Normal)
    }

    func setPlaceHolder(placeholder: String) {
        for subView in subviews{
            for subsubView in subView.subviews {
                if let textField = subsubView as? UITextField {
                    textField.attributedPlaceholder = NSAttributedString(string: placeholder, attributes: [NSForegroundColorAttributeName: Colors.White.colorWithAlphaComponent(0.5)])
                }
            }
        }
    }
}

Usage (in navigation bar)

let searchBar:SMTSearchBar = SMTSearchBar()
searchBar.sizeToFit()
searchBar.setPlaceHolder("Search for cool things")
navigationItem.titleView = searchBar
searchBar.becomeFirstResponder()
Michael
  • 9,639
  • 3
  • 64
  • 69