Changing Search Bar placeholder text colour in iOS 13 - ios

I'm trying to set colour for placeholder text inside UISearchbar. Right now I've following code. It doesn't set white colour to placeholder text on iOS 13. It works on iOS 12. It seems something is either broken or support has been removed in iOS 13?
I've searched a lot and tried few workarounds but doesn't work. I've also tried to set attributed text colour for textfield but that also doesn't change colour.
Is there a working solution for this?
class CustomSearchBar: UISearchBar {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
self.sizeToFit()
// Search text colour change
let textFieldInsideSearchBar = self.value(forKey: "searchField") as? UITextField
textFieldInsideSearchBar?.textColor = UIColor.white
// Search Placeholder text colour change
let placeHolderText = textFieldInsideSearchBar!.value(forKey: "placeholderLabel") as? UILabel
placeHolderText?.textColor = UIColor.white // doesn't work
}
}

viewDidLoad is too early
Put the code in viewDidAppear, viewDidLoad is too early. Then your placeholder should change to white
searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Enter Search Here", attributes: [NSAttributedString.Key.foregroundColor : UIColor.white])

In viewDidAppear
if iOS is higher than 13.0, use searchBar.searchTextField.attributedPlaceholder.
if iOS is lower than 13.0, use searchBar.value(forKey: "searchField") to access searchTextField
var searchBar = UISearchBar()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if #available(iOS 13.0, *) {
searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Enter Search Here", attributes: [NSAttributedString.Key.foregroundColor : UIColor.white])
} else {
if let searchField = searchBar.value(forKey: "searchField") as? UITextField {
searchField.attributedPlaceholder = NSAttributedString(string: "Enter Search Here", attributes: [NSAttributedString.Key.foregroundColor: UIColor.white2])
}
}
}

Try this:
var searchBar: UISearchBar!
if let textfield = searchBar.value(forKey: "searchField") as? UITextField {
textfield.attributedPlaceholder = NSAttributedString(string: textfield.placeholder ?? "", attributes: [NSAttributedString.Key.foregroundColor : UIColor.white])
}

Please add this extension and try this !
extension UISearchBar{
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")
}
}
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) }
}
Usage
yourSearchBarObject.setPlaceholder(textColor: .red)

You need to style everything in viewDidAppear

Related

Changing UITextField Placeholder Color for ALL textfields in appDelegate

I know you can change the placeholder text with following code:
mailTextField.attributedPlaceholder = NSAttributedString(string: "Email", attributes: [NSAttributedString.Key.foregroundColor : UIColor.black])
unfortunately, I have a lot of textfields which should look all the same. So its very annoying to copy paste this code above for every textfield.
Today I found out you can change with appearance() in appDelegate the appearance of an UIElement if you want it to be always the same. The following code works perfectly fine for labels:
UILabel.appearance().textColor = Colors.greyLabel
But when I try to do this for textfield placeholders, it does not work.
Thanks for your help.
You can create custom class which is subclass of UITextField as
import Foundation
import UIKit
class CustomTextField:UITextField{
override init(frame: CGRect) {
super.init(frame: frame)
setProperties()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setProperties()
}
func setProperties(){
backgroundColor = UIColor.white
textAlignment = .left
textColor = .black
font = UIFont.systemFont(ofSize: 15)
borderStyle = .roundedRect
if let placeholder = self.placeholder {
self.attributedPlaceholder = NSAttributedString(string:placeholder, attributes: [NSAttributedString.Key.foregroundColor: UIColor.green])
}
}
}
If you are using storyboard then you can assign that custom class to UITextField
And programatically
var mailTextField = CustomTextField()
You can use ֿappearanceWhenContainedInInstancesOfClasses but it’s available up from iOS 9.
Example:
UILabel.appearanceWhenContainedInInstancesOfClasses([UITextField.self]).textColor = UIColor.redColor()’

Replace font for every UILabel in the App

I'm trying to change the font by make extension for UILabel
extension UILabel{
var defaultFont: UIFont? {
get { return self.font }
set {
let oldFontSize = self.font.pointSize
let oldFontName = self.font.fontName
let newFontName = newValue?.fontName
self.font = UIFont(name: newFontName!, size: oldFontSize)
}
}
}
then call
UILabel.appearance().defaultFont = UIFont.init(name: "My Font", size: 5)
But always self.font is nil
using xcode 10 swift 4.2
Edit:
Now, after changing swift version to Swift3 it works good, then the problem is in Swift4.
Are there a solution or Alternative way to do that ?
try this :
is appDelegate -> didFinishLaunch:
UILabel.appearance().font = UIFont("your font name", size: 15)
Standard way of doing is to create your custom UILabel class and use this class everywhere you want that default font. e.g,
class MyFontLabel: UILabel {
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
private func commonInit() {
self.font = UIFont(name: "My Font", size: self.font.pointSize)
}
}
So while you technically can't override an instance variable, aka overriding UILabel's font variable won't work, you can extend UILabel.
public class CustomLabel: UILabel {
override public var font: UIFont! {
get { return super.font }
set {
super.font = UIFont(name: "Font-Name", size: super.font.pointSize)
}
}
}
I've found it most helpful to have add a style(as style: TypographyStyle) function in an extension to UILabel, where TypographyStyle is an enum I've defined that contains a bunch of style properties, including font. I also created a convenience init for UILabel that takes a case of TypographyStyle, and calls style(as:), so that the style will be automatically applied when the label is created.
extension UILabel {
convenience init(style: TypographyStyle) {
self.init()
self.style(as: style)
}
func style(as style: TypographyStyle) {
self.font = UIFont(name: style.fontName, size: style.fontSize)
}
}
enum TypographyStyle {
case body, h1, h2, h3
var font: UIFont {
switch self {
// here you can return different fonts for the different styles if you like
default:
return UIFont(name: "AvenirNext", size: 16)
}
}
}
// Usage
let label = UILabel(style: .body)
Put this on appDelegate in didFinishLaunchingWithOptions method:
UILabel.appearance().font = UIFont(name: "your font name", size: UILabel.appearance().font.pointSize)

Disable editing of UITextField Without disabling subviews [duplicate]

This question already has answers here:
Easy way to disable a UITextField?
(5 answers)
Closed 5 years ago.
I have UITextField and I put a UIButton in the left view of it. I want to disable editing of UITextField while my UIButton response to on click action. I tried textField.isUserInteractionEnabled and also textField.isEnabled but both of them also disable my UIButton. is there any way to do this? my custom UITextField class is like this
import UIKit
#IBDesignable
class UITextFieldWithButton: UITextField, UITextFieldDelegate {
private var happyButton: UIButton = UIButton(type: .system)
#IBInspectable
var buttonText: String {
get {
let string = happyButton.titleLabel!.text!
let start = string.index(string.startIndex, offsetBy: 2)
let end = string.endIndex
return String(happyButton.titleLabel!.text![start..<end])
}
set {
happyButton.setTitle(" " + newValue, for: .normal)
happyButton.sizeToFit()
self.leftView = happyButton
self.leftViewMode = .always
}
}
var isButtonEnable: Bool {
get {
return self.isButtonEnable
}
set {
happyButton.isEnabled = newValue
}
}
var buttonDelegate: UITextFieldWithButtonDelegate?
required override init(frame: CGRect) {
super.init(frame: frame)
delegate = self
happyButton.addTarget(self,action: #selector(pressButton(_:)), for: .touchUpInside)
happyButton.titleLabel?.font = UIFont(name: (font?.fontName)!, size: (font?.pointSize)!)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
delegate = self
happyButton.addTarget(self,action: #selector(pressButton(_:)), for: .touchUpInside)
happyButton.titleLabel?.font = UIFont(name: (font?.fontName)!, size: (font?.pointSize)!)
}
#objc private func pressButton(_ sender: UIButton){
if let click = buttonDelegate {
click.clickOnUITextFieldButton(self)
}
}
}
protocol UITextFieldWithButtonDelegate {
func clickOnUITextFieldButton(_ sender: UITextFieldWithButton)
}
"I tried textField.isUserInteractionEnabled and also textField.isEnabled but both of them also disable my UIButton" :your code is working fine you might have added button behind uitextfield try to move it forward in view hierarchy

UIButton with image on left and right side using IBDesignable in Swift 3.0

How to show left and right image on left and right side with text in UIButton using IBDesignable?
I tried to google it to find any answer but did not find any smart answer. I want to do it through storyboard. Please Help
Required Output:
You can build on this. After setting buttons in Attributes Inspector this unbutton subclass will reset the title and place images on sides (don't forget to check all edge cases):
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var button: DoubleImageButton!
override func viewDidLoad() {
super.viewDidLoad()
button.layer.borderWidth = 2.0
button.layer.borderColor = UIColor.black.cgColor
button.layer.cornerRadius = button.bounds.height*0.5
}
}
#IBDesignable
class DoubleImageButton: UIButton {
/* Inspectable properties, once modified resets attributed title of the button */
#IBInspectable var leftImg: UIImage? = nil {
didSet {
/* reset title */
setAttributedTitle()
}
}
#IBInspectable var rightImg: UIImage? = nil {
didSet {
/* reset title */
setAttributedTitle()
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setAttributedTitle()
}
override init(frame: CGRect) {
super.init(frame: frame)
setAttributedTitle()
}
private func setAttributedTitle() {
var attributedTitle = NSMutableAttributedString()
/* Attaching first image */
if let leftImg = leftImg {
let leftAttachment = NSTextAttachment(data: nil, ofType: nil)
leftAttachment.image = leftImg
let attributedString = NSAttributedString(attachment: leftAttachment)
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString)
if let title = self.currentTitle {
mutableAttributedString.append(NSAttributedString(string: title))
}
attributedTitle = mutableAttributedString
}
/* Attaching second image */
if let rightImg = rightImg {
let leftAttachment = NSTextAttachment(data: nil, ofType: nil)
leftAttachment.image = rightImg
let attributedString = NSAttributedString(attachment: leftAttachment)
let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString)
attributedTitle.append(mutableAttributedString)
}
/* Finally, lets have that two-imaged button! */
self.setAttributedTitle(attributedTitle, for: .normal)
}
}
In attributes inspector:
Result (adjust my implementation to achieve the best result):
Be sure to check cases when you'll have only right image selected for example but no left image etc. It's a quick solution and not fully tested.
Have fun and good luck! ;]

Custom UISearchBar with UISearchController

The documentation for UISearchController says that you can override - searchBar to provide a custom subclass of UISearchBar for the controller to use. The custom search bar does get used, and its own delegate methods are called correctly, but the UISearchResultsUpdating method no longer gets called when the search bar changes. Do I need to do a lot of wiring things up manually, or is there something I'm missing to get the controller to behave like it does with a natively supplied search bar?
Override the SearchBar getter in your custom UISearchController class, it have to return your custom SearchBar and it have to be already initialized, then you setup its properties only after the UISearchController init, this way all the UISearchController functionality are retained:
public class DSearchController: UISearchController {
private var customSearchBar = DSearchBar()
override public var searchBar: UISearchBar {
get {
return customSearchBar
}
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
public init(searchResultsController: UIViewController?,
searchResultsUpdater: UISearchResultsUpdating?,
delegate: UISearchControllerDelegate?,
dimsBackgroundDuringPresentation: Bool,
hidesNavigationBarDuringPresentation: Bool,
searchBarDelegate: UISearchBarDelegate?,
searchBarFrame: CGRect?,
searchBarStyle: UISearchBarStyle,
searchBarPlaceHolder: String,
searchBarFont: UIFont?,
searchBarTextColor: UIColor?,
searchBarBarTintColor: UIColor?, // Bar background
searchBarTintColor: UIColor) { // Cursor and bottom line
super.init(searchResultsController: searchResultsController)
self.searchResultsUpdater = searchResultsUpdater
self.delegate = delegate
self.dimsBackgroundDuringPresentation = dimsBackgroundDuringPresentation
self.hidesNavigationBarDuringPresentation = hidesNavigationBarDuringPresentation
customSearchBar.setUp(searchBarDelegate,
frame: searchBarFrame,
barStyle: searchBarStyle,
placeholder: searchBarPlaceHolder,
font: searchBarFont,
textColor: searchBarTextColor,
barTintColor: searchBarBarTintColor,
tintColor: searchBarTintColor)
}
}
And this is my custom searchBar:
public class DSearchBar: UISearchBar {
var preferredFont: UIFont?
var preferredTextColor: UIColor?
init(){
super.init(frame: CGRect.zero)
}
func setUp(delegate: UISearchBarDelegate?,
frame: CGRect?,
barStyle: UISearchBarStyle,
placeholder: String,
font: UIFont?,
textColor: UIColor?,
barTintColor: UIColor?,
tintColor: UIColor?) {
self.delegate = delegate
self.frame = frame ?? self.frame
self.searchBarStyle = searchBarStyle
self.placeholder = placeholder
self.preferredFont = font
self.preferredTextColor = textColor
self.barTintColor = barTintColor ?? self.barTintColor
self.tintColor = tintColor ?? self.tintColor
self.bottomLineColor = tintColor ?? UIColor.clearColor()
sizeToFit()
// translucent = false
// showsBookmarkButton = false
// showsCancelButton = true
// setShowsCancelButton(false, animated: false)
// customSearchBar.backgroundImage = UIImage()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
let bottomLine = CAShapeLayer()
var bottomLineColor = UIColor.clearColor()
override public func layoutSubviews() {
super.layoutSubviews()
for view in subviews {
if let searchField = view as? UITextField { setSearchFieldAppearance(searchField); break }
else {
for sView in view.subviews {
if let searchField = sView as? UITextField { setSearchFieldAppearance(searchField); break }
}
}
}
bottomLine.path = UIBezierPath(rect: CGRectMake(0.0, frame.size.height - 1, frame.size.width, 1.0)).CGPath
bottomLine.fillColor = bottomLineColor.CGColor
layer.addSublayer(bottomLine)
}
func setSearchFieldAppearance(searchField: UITextField) {
searchField.frame = CGRectMake(5.0, 5.0, frame.size.width - 10.0, frame.size.height - 10.0)
searchField.font = preferredFont ?? searchField.font
searchField.textColor = preferredTextColor ?? searchField.textColor
//searchField.backgroundColor = UIColor.clearColor()
//backgroundImage = UIImage()
}
}
Init example:
searchController = DSearchController(searchResultsController: ls,
searchResultsUpdater: self,
delegate: self,
dimsBackgroundDuringPresentation: true,
hidesNavigationBarDuringPresentation: true,
searchBarDelegate: ls,
searchBarFrame: CGRectMake(0.0, 0.0, SCREEN_WIDTH, 44.0),
searchBarStyle: .Minimal,
searchBarPlaceHolder: NSLocalizedString("Search a location...", comment: ""),
searchBarFont: nil,
searchBarTextColor: nil,
searchBarBarTintColor: UIColor.whiteColor(),
searchBarTintColor: iconsColor)
searchController.searchBar.keyboardAppearance = .Dark
definesPresentationContext = true
tableView.tableHeaderView = searchController.searchBar
This is a known bug. Unfortunately, there is no workaround that does not involve private API.
When you subclass UISearchController you can customise UISearchBar in getter (setter doesn't exist).
Example - in subclass implementation:
-(UISearchBar*)searchBar{
UISearchBar *baseSearchBar = [super searchBar];
if (baseSearchBar.showsScopeBar) {
baseSearchBar.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 88);
}else{
baseSearchBar.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 44);
}
return baseSearchBar;
}
Hope this helps someone.
I think it's supposed to behave like that.
This is from UISearchController.h
// You are free to become the search bar's delegate to monitor for text changes and button presses.
#property (nonatomic, retain, readonly) UISearchBar *searchBar;
All that delegate method (updateSearchResultsForSearchController:) does is return your search controller so you can access its search bar.
You can just do that through your custom search bar delegate methods.

Resources