iOS 13 Set UISearchTextField placeholder color iOS 13 Set UISearchTextField placeholder color ios ios

iOS 13 Set UISearchTextField placeholder color


I found the following code snippet worked:

DispatchQueue.main.asyncAfter(deadline: .now()+0.5) {    // Stackoverflow said the above doesn't work on viewDidLoad style methods, so running it async after a 0.5s delay to workaround.  Works.    searchField.attributedPlaceholder = NSAttributedString(string:"Search", attributes: attributesDictionary)}


You can try this with Xcode 11, iOS 13

 extension UISearchBar {        func getTextField() -> UITextField? { return value(forKey: "searchField") as? UITextField }        func set(textColor: UIColor) { if let textField = getTextField() { textField.textColor = textColor } }        func setPlaceholder(textColor: UIColor) { getTextField()?.setPlaceholder(textColor: textColor) }        func setClearButton(color: UIColor) { getTextField()?.setClearButton(color: color) }        func setTextField(color: UIColor) {            guard let textField = getTextField() else { return }            switch searchBarStyle {            case .minimal:                textField.layer.backgroundColor = color.cgColor                textField.layer.cornerRadius = 8            case .prominent, .default: textField.backgroundColor = color            @unknown default: break            }        }        func setSearchImage(color: UIColor) {            guard let imageView = getTextField()?.leftView as? UIImageView else { return }            imageView.tintColor = color            imageView.image = imageView.image?.withRenderingMode(.alwaysTemplate)        }    }    private extension UITextField {    private class Label: UILabel {        private var _textColor = UIColor.lightGray        override var textColor: UIColor! {            set { super.textColor = _textColor }            get { return _textColor }        }        init(label: UILabel, textColor: UIColor = .lightGray) {            _textColor = textColor            super.init(frame: label.frame)            self.text = label.text            self.font = label.font        }        required init?(coder: NSCoder) { super.init(coder: coder) }    }    private class ClearButtonImage {        static private var _image: UIImage?        static private var semaphore = DispatchSemaphore(value: 1)        static func getImage(closure: @escaping (UIImage?)->()) {            DispatchQueue.global(qos: .userInteractive).async {                semaphore.wait()                DispatchQueue.main.async {                    if let image = _image { closure(image); semaphore.signal(); return }                    guard let window = UIApplication.shared.windows.first else { semaphore.signal(); return }                    let searchBar = UISearchBar(frame: CGRect(x: 0, y: -200, width: UIScreen.main.bounds.width, height: 44))                    window.rootViewController?.view.addSubview(searchBar)                    searchBar.text = ""                    searchBar.layoutIfNeeded()                    _image = searchBar.getTextField()?.getClearButton()?.image(for: .normal)                    closure(_image)                    searchBar.removeFromSuperview()                    semaphore.signal()                }            }        }    }    func setClearButton(color: UIColor) {        ClearButtonImage.getImage { [weak self] image in            guard   let image = image,                let button = self?.getClearButton() else { return }            button.imageView?.tintColor = color            button.setImage(image.withRenderingMode(.alwaysTemplate), for: .normal)        }    }    var placeholderLabel: UILabel? { return value(forKey: "placeholderLabel") as? UILabel }    func setPlaceholder(textColor: UIColor) {        guard let placeholderLabel = placeholderLabel else { return }        let label = Label(label: placeholderLabel, textColor: textColor)        placeholderLabel.removeFromSuperview() // To remove existing label. Otherwise it will overwrite it if called multiple times.        setValue(label, forKey: "placeholderLabel")    }    func getClearButton() -> UIButton? { return value(forKey: "clearButton") as? UIButton }}

Use:

    searchBarObj.placeholder = "placeholder text"    searchBarObj.set(textColor: .blue)    searchBarObj.setTextField(color: UIColor.gray)    searchBarObj.setPlaceholder(textColor: .red)    searchBarObj.setSearchImage(color: .black)    searchBarObj.setClearButton(color: .red)


iOS 13

Use a custom search bar.

This also works when part of a UISearchController inside a UINavigationItem (with hidesSearchBarWhenScrolling = true).

We want to apply our changes immediately after UIAppearance proxies are being applied since those are the most likely root cause:

class MySearchBar : UISearchBar {    // Appearance proxies are applied when a view is added to a view hierarchy, so apply your tweaks after that:    override func didMoveToSuperview() {        super.didMoveToSuperview() // important! - system colors will not apply correctly on ios 11-12 without this        let placeholderColor = UIColor.white.withAlphaComponent(0.75)        let placeholderAttributes = [NSAttributedString.Key.foregroundColor : placeholderColor]        let attributedPlaceholder = NSAttributedString(string: "My custom placeholder", attributes: placeholderAttributes)        self.searchTextField.attributedPlaceholder = attributedPlaceholder                // Make the magnifying glass the same color        (self.searchTextField.leftView as? UIImageView)?.tintColor = placeholderColor    }}// Override `searchBar` as per the documentationprivate class MySearchController : UISearchController {    private lazy var customSearchBar = MySearchBar()    override var searchBar: UISearchBar { customSearchBar }}

Copy of my answer from here