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 }}