Can I change colour/image UISearchBar clear button?
Asked Answered
M

5

5

enter image description here

I want to change clear button color to white. I have tried many ways but no luck.:( I also refer the following link. But it does not work for me.

Please find below code which I have tried. I am working on latest ios 11. appreciate any help.

class SearchBar: UISearchBar {

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

        sharedInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        sharedInit()
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        sharedInit()
    }

    private func sharedInit() {
        self.placeholder = "Search"
        self.setTextColor(color: .white)
        self.setTextFieldColor(color: UIColor.hexString("549C64"))
        self.setPlaceholderTextColor(color: .white)
        self.setSearchImageColor(color: .white)
        self.setTextFieldClearButtonColor(color: .white)

        if let textfield = self.value(forKey: "searchField") as? UITextField {
            if let backgroundview = textfield.subviews.first {
                backgroundview.layer.cornerRadius = 18.0;
                backgroundview.clipsToBounds = true;
            }
            //textfield.frame = CGRect(x: self.frame.origin.x, y: self.frame.origin.y, width: self.frame.size.width, height: 10.0)
        }


        //NO LUCK FOR HEIGHT
        for subView in self.subviews  {
            for subsubView in subView.subviews  {
                if let textField = subsubView as? UITextField {
                    for subsubView in textField.subviews  {
                        if let btn = subsubView as? UIButton {
                            print(btn)
                        }
                    }

                }else  if let btn = subsubView as? UIButton {
                    print(btn)
                }
            }
            if let btn = subView as? UIButton {
                print(btn)
            }
        }

    }
}

extension UISearchBar {

    private func getViewElement<T>(type: T.Type) -> T? {
        let svs = subviews.flatMap { $0.subviews }
        guard let element = (svs.filter { $0 is T }).first as? T else { return nil }
        return element
    }

    func getSearchBarTextField() -> UITextField? {
        return getViewElement(type: UITextField.self)
    }

    func getSearchBarButton() -> UIButton? {
        return getViewElement(type: UIButton.self)
    }

    func setTextColor(color: UIColor) {
        if let textField = getSearchBarTextField() {
            textField.textColor = color
        }
    }

    func setTextFieldColor(color: UIColor) {
        if let textField = getViewElement(type: UITextField.self) {
            switch searchBarStyle {
            case .minimal:
                textField.layer.backgroundColor = color.cgColor
                textField.layer.cornerRadius = 18.0
                if let backgroundview = textField.subviews.first {
                    backgroundview.layer.cornerRadius = 18.0;
                    backgroundview.clipsToBounds = true;
                }
            case .prominent, .default:
                textField.backgroundColor = color
            }
        }
    }

    func setPlaceholderTextColor(color: UIColor) {
        if let textField = getSearchBarTextField() {
            textField.attributedPlaceholder = NSAttributedString(string: self.placeholder != nil ? self.placeholder! : "", attributes: [NSAttributedStringKey.foregroundColor: color])
        }
    }

    func setTextFieldClearButtonColor(color: UIColor) {
        if let textField = getSearchBarTextField() {
            let button = textField.value(forKey: "_clearButton") as! UIButton
            if let image = button.imageView?.image {
                button.setImage(image.transform(withNewColor: color), for: .normal)
            }else{
                //button.setImage(#imageLiteral(resourceName: "icon-hotel"), for: .normal)
            }
        }
        if let btn = getSearchBarButton() {
            let button = btn.value(forKey: "_clearButton") as! UIButton
            if let image = button.imageView?.image {
                button.setImage(image.transform(withNewColor: color), for: .normal)
            }else{
                //button.setImage(#imageLiteral(resourceName: "icon-hotel"), for: .normal)
            }
        }

    }

    func setSearchImageColor(color: UIColor) {
        if let imageView = getSearchBarTextField()?.leftView as? UIImageView {
            imageView.image = imageView.image?.transform(withNewColor: color)
        }
    }
}

extension UIImage {

    func transform(withNewColor color: UIColor) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(size, false, scale)

        let context = UIGraphicsGetCurrentContext()!
        context.translateBy(x: 0, y: size.height)
        context.scaleBy(x: 1.0, y: -1.0)
        context.setBlendMode(.normal)

        let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
        context.clip(to: rect, mask: cgImage!)

        color.setFill()
        context.fill(rect)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        return newImage
    }
}
Maleki answered 17/5, 2018 at 5:10 Comment(3)
Did you try this way medium.com/@superpeteblaze/…?Isola
Look at this #27945281Corking
yes, I have checked both, But not working for me.Maleki
M
7

I have found the answer please find below code for the same

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {

        if let searchTextField = searchBar.value(forKey: "searchField") as? UITextField , let clearButton = searchTextField.value(forKey: "_clearButton")as? UIButton {

            if let img3 = clearButton.image(for: .highlighted) {
                clearButton.isHidden = false
                let tintedClearImage = img3.imageWithColor(color1: UIColor.white)
                clearButton.setImage(tintedClearImage, for: .normal)
                clearButton.setImage(tintedClearImage, for: .highlighted)
            }else{
               clearButton.isHidden = true
            }               
        }            
    }

enter image description here Add following extension for Image color change in your project.

extension UIImage {
    func imageWithColor(color1: UIColor) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
        color1.setFill()

        let context = UIGraphicsGetCurrentContext()
        context?.translateBy(x: 0, y: self.size.height)
        context?.scaleBy(x: 1.0, y: -1.0)
        context?.setBlendMode(CGBlendMode.normal)

        let rect = CGRect(origin: .zero, size: CGSize(width: self.size.width, height: self.size.height))
        context?.clip(to: rect, mask: self.cgImage!)
        context?.fill(rect)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage!
    }
}
Maleki answered 17/5, 2018 at 8:41 Comment(7)
is not reflect in first timePrimateship
@Primateship This could be a threading issue, where the callback is called before the image is actually initialized. I used DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { ... } and it fixed the issue.Betts
@Betts - i will try and let u knowPrimateship
@Primateship For me the color of the cancel icon was unfortunately changed back to the gray one once I returned from another screen, so there are still some rough edges to this solutionBetts
self.value(forKey: "_clearButton") as? UIButton seems like undocumented behavior and could change on any Apple SDK update. I would not advise using that.Capote
@wilc0, maybe you are right. Do you have a better solution? Can you please share it here?Maleki
The best solution is to provide your own image, similar to what the other answer shows: searchBar.setImage(UIImage(named: "<your_image_name>"), for: .clear, state: .normal). That way you control the color, image, and supply it to the search bar. If you are bent against using your own image, then you can iterate through the uisearchbar view hierarchy and find the clear button that way (much safer then referencing it by an undocumented name). There is a good example of that here: https://mcmap.net/q/747401/-how-to-change-the-tint-color-of-the-clear-button-on-a-uitextfieldCapote
W
14

You can use custom icons in iOS 11:

Code:

searchBar.setImage(UIImage(named: "ic_clear"), for: .clear, state: .normal)
Whensoever answered 10/9, 2018 at 12:38 Comment(2)
It is also nice to combine it with the accepted answer's UIImage extension. Then you can change the image color dependent on the situation. (set your image to "template image" mode)Celebration
not working on iOS17Hallux
M
7

I have found the answer please find below code for the same

func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {

        if let searchTextField = searchBar.value(forKey: "searchField") as? UITextField , let clearButton = searchTextField.value(forKey: "_clearButton")as? UIButton {

            if let img3 = clearButton.image(for: .highlighted) {
                clearButton.isHidden = false
                let tintedClearImage = img3.imageWithColor(color1: UIColor.white)
                clearButton.setImage(tintedClearImage, for: .normal)
                clearButton.setImage(tintedClearImage, for: .highlighted)
            }else{
               clearButton.isHidden = true
            }               
        }            
    }

enter image description here Add following extension for Image color change in your project.

extension UIImage {
    func imageWithColor(color1: UIColor) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
        color1.setFill()

        let context = UIGraphicsGetCurrentContext()
        context?.translateBy(x: 0, y: self.size.height)
        context?.scaleBy(x: 1.0, y: -1.0)
        context?.setBlendMode(CGBlendMode.normal)

        let rect = CGRect(origin: .zero, size: CGSize(width: self.size.width, height: self.size.height))
        context?.clip(to: rect, mask: self.cgImage!)
        context?.fill(rect)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage!
    }
}
Maleki answered 17/5, 2018 at 8:41 Comment(7)
is not reflect in first timePrimateship
@Primateship This could be a threading issue, where the callback is called before the image is actually initialized. I used DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { ... } and it fixed the issue.Betts
@Betts - i will try and let u knowPrimateship
@Primateship For me the color of the cancel icon was unfortunately changed back to the gray one once I returned from another screen, so there are still some rough edges to this solutionBetts
self.value(forKey: "_clearButton") as? UIButton seems like undocumented behavior and could change on any Apple SDK update. I would not advise using that.Capote
@wilc0, maybe you are right. Do you have a better solution? Can you please share it here?Maleki
The best solution is to provide your own image, similar to what the other answer shows: searchBar.setImage(UIImage(named: "<your_image_name>"), for: .clear, state: .normal). That way you control the color, image, and supply it to the search bar. If you are bent against using your own image, then you can iterate through the uisearchbar view hierarchy and find the clear button that way (much safer then referencing it by an undocumented name). There is a good example of that here: https://mcmap.net/q/747401/-how-to-change-the-tint-color-of-the-clear-button-on-a-uitextfieldCapote
C
1

Here is My solution: My codes try to change ios textfield clear button color.

But in some IOS versions UIButton for key "_clearButton" return nil Cuz of it registered in high IOS Versions. I have tested my extensions it worked in IOS 14, but in IOS 12.4 textfield clear button was not changed color. I debuged and recognize that forKey: "_clearButton" return nil in IOS 12.4

My solution tries get clearbutton image if it is nil use custom image and change color of it

extension UITextField{
    var clearButton : UIButton{
        return self.value(forKey: "_clearButton") as! UIButton

    }
    
    var clearButtonTintColor: UIColor? {
           get {
               return clearButton.tintColor
           }
           set {
             var image = clearButton.imageView?.image
                
            if image == nil{
                image = UIImage(named: "clear_field")//this is custom image
            }
        
                image =  image?.withRenderingMode(.alwaysTemplate)
               clearButton.setImage(image, for: .normal)
               clearButton.tintColor = newValue
         
           }
       }
}

to change color use this:

textField.clearButtonTintColor = UIColor(rgb: 0x753a3a)

Here is a similiar official clear button image

Capybara answered 1/7, 2021 at 12:25 Comment(0)
H
0

Update with Swift 5.7

You can add an extension to UIImage to change the tint color of an image.

    extension UIImage {
    func imageWithColor(color1: UIColor) -> UIImage {
        UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
        color1.setFill()

        let context = UIGraphicsGetCurrentContext()
        context?.translateBy(x: 0, y: self.size.height)
        context?.scaleBy(x: 1.0, y: -1.0)
        context?.setBlendMode(CGBlendMode.normal)

        let rect = CGRect(origin: .zero, size: CGSize(width: self.size.width, height: self.size.height))
        context?.clip(to: rect, mask: self.cgImage!)
        context?.fill(rect)

        let newImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return newImage!
    }

Then configure the close button with its tint color depending on the Theme color you have(optional).

     setImage(UIImage(named:"searchbarClear")?.imageWithColor(color1: ThemeManager.shared.currentTheme.searchBarImageTintColor), for: .clear, state: .normal)
Homunculus answered 21/7, 2022 at 19:19 Comment(0)
N
-1

How to change clearButton’s Color?

iOS 11 Solution :

Unfortunately, in iOS 11 we cannot change the clear button’s the rendering mode programmatically(I have no idea why). First set your new clear icon image programmatically and then go to the Assets folder and change your icon’s rendering mode manually.

let clearButton = textFieldInsideSearchBar?.value(forKey: “clearButton”) as! UIButton
clearButton.setImage(UIImage(named: "ic_clear"), for: .normal)
clearButton.tintColor = .white

iOS 10 and Below:

let clearButton = textFieldInsideSearchBar?.value(forKey: “clearButton”) as! UIButton
clearButton.setImage(clearButton.imageView?.image?.withRenderingMode(.alwaysTemplate), for: .normal)
clearButton.tintColor = UIColor.white
Newcomer answered 1/7, 2021 at 5:57 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.