How to customise UISearchController? - swift

I'm using a UISearchController in my application but I can't figure a way to customise it. I looked here in Stackoverflow but none of the confirmed answers worked for me. I tried looking in more places but nothing works.
This is my code:
import UIKit
class MainVC: UIViewController {
lazy var mSearchBarController: UISearchController = {
let mSearchBar = UISearchController(searchResultsController: nil)
mSearchBar.searchBar.barStyle = .default
mSearchBar.searchBar.placeholder = "enter city here"
mSearchBar.searchBar.tintColor = UIColor.white
return mSearchBar
}()
override func viewDidLayoutSubviews() {
setupSearchBar()
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(red: 80/255, green: 135/255, blue: 179/255, alpha: 1.0)
setupNavBar()
self.navigationItem.searchController = mSearchBarController
}
private func setupNavBar(){
navigationItem.title = "Home"
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.hidesSearchBarWhenScrolling = true
}
private func setupSearchBar(){
let mSearchTextField = mSearchBarController.searchBar.value(forKey: "searchField") as? UITextField
mSearchTextField?.textColor = UIColor(red: 255/255, green: 245/255, blue: 139/255, alpha: 1.0)
let mAttributedPlaceholder = NSMutableAttributedString(string: "enter city here", attributes: [NSAttributedString.Key.foregroundColor : UIColor(red: 255/255, green: 245/255, blue: 139/255, alpha: 1.0)])
mSearchTextField?.attributedPlaceholder = mAttributedPlaceholder
for view in mSearchBarController.searchBar.subviews {
for subview in view.subviews {
if let textField = subview as? UITextField {
textField.backgroundColor = UIColor.red
textField.textColor = UIColor.white
}
}
}
}
}
I can't figure a way to change the textColor of the searchBar nor the backgroundColor of it.
This is what I get:

See I use this simple code to change the textColor and backgroundColor of seacrhBar:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var searchBar: UISearchBar!
override func viewDidLoad() {
super.viewDidLoad()
for view in searchBar.subviews {
for subview in view.subviews {
if let textField = subview as? UITextField {
textField.backgroundColor = UIColor.red
textField.textColor = UIColor.white
}
}
}
}
for reference you can check:-
How can I change the UISearchBar search text color? (for textcolor)
http://jitu1990.blogspot.com/2017/06/textcolor-and-backgroundcolor.html (for backgorund color)

Related

Problem with background in table when emulating Iphone Max Pro 12

I'm trying to emulate my app in different Iphone models but I'm also having the same mistake when I use a Max Pro 12. Anytime I use a tableViewContoller it seems not to fill the background cells completely. Just check my pictures. In the rest of models, including Max Pro 11 it's working ok. I don`t have any constraints because the I can't add them when I use tableViewController. Any suggestion?
Here you have my code.
import UIKit
import FirebaseAuth
class MenuCustomCellController: UITableViewCell {
#IBOutlet weak var imEvento: UIImageView!
#IBOutlet weak var txtNombreEvento: UILabel!
#IBOutlet weak var txtFechaEvento: UILabel!
#IBOutlet weak var txtEstadoEvento: UILabel!
}
class MenuInicialTableController: UITableViewController {
#IBOutlet weak var celdaUsuarios: UITableViewCell!
#IBOutlet weak var celdaEventos: UITableViewCell!
#IBOutlet weak var celdaBuscarEventos: UITableViewCell!
#IBOutlet weak var celdaGraficos: UITableViewCell!
#IBOutlet weak var celdaCerrar: UITableViewCell!
#IBOutlet weak var celdaAyuda: UITableViewCell!
override func viewWillAppear(_ animated: Bool) {
let tap = UITapGestureRecognizer(target: self, action: #selector(MenuInicialTableController.tapFunction))
celdaCerrar.isUserInteractionEnabled = true
celdaCerrar.addGestureRecognizer(tap)
let background = UIView()
tableView.backgroundView = background
tableView.backgroundView?.aplicarFondoDegradado()
tableView.layer.borderWidth = 2
tableView.layer.borderColor = UIColor.white.cgColor
tableView.layer.cornerRadius = 10
celdaUsuarios.layer.borderWidth = 1
celdaUsuarios.layer.borderColor = UIColor.white.cgColor
celdaUsuarios.layer.cornerRadius = 10
celdaUsuarios.contentView.aplicarFondoMenuUsuarios()
celdaEventos.layer.borderWidth = 1
celdaEventos.layer.borderColor = UIColor.white.cgColor
celdaEventos.layer.cornerRadius = 10
celdaEventos.contentView.aplicarFondoMenuEventos()
celdaGraficos.layer.borderWidth = 1
celdaGraficos.layer.borderColor = UIColor.white.cgColor
celdaGraficos.layer.cornerRadius = 10
celdaGraficos.contentView.aplicarFondoMenuEventos()
celdaCerrar.layer.borderWidth = 1
celdaCerrar.layer.borderColor = UIColor.white.cgColor
celdaCerrar.layer.cornerRadius = 10
celdaCerrar.contentView.aplicarFondoMenuUsuarios()
}
override func viewDidLoad() {
super.viewDidLoad()
self.title = "MENÚ PRINCIPAL"
self.navigationItem.setHidesBackButton(true, animated: true)
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
//Dos filas por sección
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
#objc
func tapFunction(sender:UITapGestureRecognizer) {
exit(0)
}
}
And here you have how I apply gradients:
func aplicarFondoMenuUsuarios() {
let colorInicio = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1.0).cgColor
let colorFin = UIColor(red: 0/255, green: 190/255, blue: 219/255, alpha: 1.0).cgColor
if let gradientLayer = layer.sublayers?.first as? CAGradientLayer {
gradientLayer.colors = [colorInicio,colorFin]
}
else{
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [colorInicio, colorFin]
gradientLayer.startPoint = CGPoint(x: 0, y: 0.50)
gradientLayer.endPoint = CGPoint(x: 0.50, y: 1.00)
gradientLayer.frame = self.bounds
self.layer.insertSublayer(gradientLayer, at:0)
}
func aplicarFondoMenuEventos() {
let colorInicio = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1.0).cgColor
let colorFin = UIColor(red: 15/255, green: 0/255, blue: 219/255, alpha: 1.0).cgColor
if let gradientLayer = layer.sublayers?.first as? CAGradientLayer {
gradientLayer.colors = [colorInicio,colorFin]
}
else{
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [colorInicio, colorFin]
gradientLayer.startPoint = CGPoint(x: 0, y: 0.50)
gradientLayer.endPoint = CGPoint(x: 0.50, y: 1.00)
gradientLayer.frame = self.bounds
self.layer.insertSublayer(gradientLayer, at:0)
}
}
Don't use viewWillAppear for layout-code. And not for TapGestures either. Move the three first lines (tap-related) into viewDidLoad, and put the rest into override func viewDidLayoutSubviews(){}.
There are a lot of other stuff you should do as well, like subclassing the cells and have them perform their gradients at the time of their own layout, and you should also remove let background = UIView() and instead just say tableView.backgroundView = UIView() in viewDidLoad (not in willAppear and not in viewDidLayoutSubviews).

Is it possible to customize UINavigationBar by protocol?

I want to customize UINavigationBar for some view controllers. For some reasons, I cannot just simply extend UIViewController. So I was trying to do it by a protocol.
What I have tried:
protocol TransparentNavigationBarProtocol {
func makeNavigationBarTransparent()
}
extension TransparentNavigationBarProtocol where Self: UIViewController {
func makeNavigationBarTransparent() {
if let navController = navigationController {
navController.navigationBar.setBackgroundImage(UIImage(), for: .default)
navController.navigationBar.shadowImage = UIImage()
navController.navigationBar.tintColor = UIColor.white
navController.navigationBar.barStyle = .blackTranslucent
navController.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
navController.navigationBar.backgroundColor = .clear
}
}
}
I added some breakpoints which show the function had been called successfully but the navigationBar didn't change. So I was wondering is it possible to achieve this by protocols?
For Xcode11, you need set a image, not nil
Also, in your viewcontroller's viewWillAppear, you need call makeNavigationBarTransparent()
func makeNavigationBarTransparent() {
if let navController = navigationController {
navController.navigationBar.setBackgroundImage(UIImage(), for: .default)
navController.navigationBar.shadowImage = UIImage()
navController.navigationBar.tintColor = UIColor.init(red: 74/255, green: 74/255, blue: 74/255, alpha: 1)
navController.navigationBar.barStyle = .default
navController.navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor(red: 74/255, green: 74/255, blue: 74/255, alpha: 1)]
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
makeNavigationBarTransparent()
}

How can I get a textview to trigger a scrollview to move up for the keyboard?

I'm creating a form that lives on a view within a scrollview. It contains two textFields and one textView. I've cobbled together some code from online resources(like this one) so that it scrolls up if a textField is ever too low on the view/obstructed by the keyboard. The problem is that it doesn't seem to work for textViews.
I've done my best to comprehend why textViews won't trigger the same behavior, but I could really use some help. Here's my VC code:
import UIKit
class AddAPlaceBasicInfoViewController: UIViewController, UITextFieldDelegate, UITextViewDelegate {
// Outlets
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var categoryTextField: UITextField!
#IBOutlet weak var descriptionTextView: UITextView!
#IBOutlet weak var nextButton: UIButton!
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(noti:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(noti:)), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
// Default to an disabled "Next" button.
nextButton.isEnabled = false
nextButton.backgroundColor = UIColor(red: 89/255, green: 89/255, blue: 89/255, alpha: 1/1)
// Check that fields are filled.
nameTextField.delegate = self
descriptionTextView.delegate = self
// Helper functions.
createToolbar()
descriptionTextViewSetup(descriptionTextView)
}
// Creates the done button above the category picker.
func createToolbar() {
// Creates an instance of UIToolbar() named "toolbar".
let toolbar = UIToolbar()
toolbar.sizeToFit()
// Set up button properties.
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(AddAPlaceBasicInfoViewController.dismissKeyboard))
// Set color to teal.
doneButton.tintColor = UIColor(red: 0/255, green: 134/255, blue: 111/255, alpha: 1)
// Set up button in the toolbar and make it interactive.
toolbar.setItems([doneButton], animated: false)
toolbar.isUserInteractionEnabled = true
// Add the toolbar as an accessory view to the category picker.
nameTextField.inputAccessoryView = toolbar
categoryTextField.inputAccessoryView = toolbar
descriptionTextView.inputAccessoryView = toolbar
// Set toolbar's background to white.
toolbar.backgroundColor = UIColor.white
}
// Function to dismiss the keyboard. Used when the user taps the "Done" button in the toolbar.
func dismissKeyboard() {
view.endEditing(true)
if nameTextField.text?.characters.count == 0 || categoryTextField.text?.characters.count == 0 || descriptionTextView.text == "What's this place like? (You'll be able to add a photo on the next screen)" {
nextButton.isEnabled = false
nextButton.backgroundColor = UIColor(red: 89/255, green: 89/255, blue: 89/255, alpha: 1/1)
} else {
nextButton.isEnabled = true
nextButton.backgroundColor = UIColor(red: 0/255, green: 134/255, blue: 111/255, alpha: 1/1)
}
}
// Function to create placeholder text.
func descriptionTextViewSetup(_ textView: UITextView) {
// Modifications
descriptionTextView.text = "What's this place like? (You'll be able to add a photo on the next screen)"
descriptionTextView.textColor = UIColor(red: 199/255, green: 199/255, blue: 205/255, alpha: 1/1)
descriptionTextView.textContainer.lineFragmentPadding = 0
//descriptionTextView.textColor = UIColor.lightGray
}
//---------------------------------
// MARK: - Notification Center
//---------------------------------
func keyboardWillHide(noti: Notification) {
let contentInsets = UIEdgeInsets.zero
scrollView.contentInset = contentInsets
scrollView.scrollIndicatorInsets = contentInsets
}
func keyboardWillShow(noti: Notification) {
guard let userInfo = noti.userInfo else { return }
guard var keyboardFrame: CGRect = (userInfo[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue else { return }
keyboardFrame = self.view.convert(keyboardFrame, from: nil)
var contentInset:UIEdgeInsets = scrollView.contentInset
contentInset.bottom = keyboardFrame.size.height
scrollView.contentInset = contentInset
}
}
If you are creating a Form for users where they can enter information then I would suggest using a static UITableView. Static UITableView can be used if your UIViewController is of type UITableViewController. Static TableView makes it very easy to place controls and since UITableView already has a UIScrollView it automatically scrolls when the UITextView is in focus.
Try it out:

How to Programmatically show UISearchController in UINavigationController's leftbarbuttonitem and hide it after search?

I want to show a UISearchController within UINavigationController, when clicked on UINavigationController's rightbarbuttonitem and hide it when clicked on the button again.
Initially the UINavigationBar will be like this
Later,
The UINavigationBar should look like this, when search is clicked
and further when clicked on close icon, the UINavigationBar should look like in image1.
The code I'm using to show searchcontroller is,
func setUpSearchBar(){
self.searchController = UISearchController(searchResultsController: nil)
self.searchController.searchBar.showsCancelButton = true
var cancelButton: UIButton
let topView: UIView = self.searchController.searchBar.subviews[0] as UIView
for subView in topView.subviews {
if let pvtClass = NSClassFromString("UINavigationButton") {
if subView.isKind(of: pvtClass) {
cancelButton = subView as! UIButton
cancelButton.setTitle("", for: .normal)
cancelButton.tintColor = UIColor.blue
cancelButton.setImage(UIImage(named:"close"), for: .normal)
cancelButton.addTarget(self, action: #selector(pressButton(button:)), for: .touchUpInside)
}
}
}
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).defaultTextAttributes = [NSFontAttributeName:UIFont(name:"Futura-Medium", size:20)!,NSForegroundColorAttributeName:UIColor(red: 234/255, green: 234/255, blue: 234/255, alpha: 1.0)]
self.searchController.searchResultsUpdater = self
self.searchController.delegate = self
self.searchController.searchBar.delegate = self
self.searchController.hidesNavigationBarDuringPresentation = false
self.searchController.dimsBackgroundDuringPresentation = true
self.searchController.searchBar.tintColor = UIColor(red: 56/255, green: 192/255, blue: 201/255, alpha: 1.0)
self.searchController.searchBar.backgroundColor = UIColor.clear
self.searchController.searchBar.barStyle = .black
// self.navigationItem.titleView = searchController.searchBar
let leftNavBarButton = UIBarButtonItem(customView: self.searchController.searchBar)
self.navigationItem.leftBarButtonItem = leftNavBarButton
self.searchController.searchBar.becomeFirstResponder()
}
func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
self.searchController.searchBar.resignFirstResponder()
self.navigationItem.leftBarButtonItem?.isEnabled = false
self.navigationItem.leftBarButtonItem?.tintColor = .clear
}
func pressButton(button: UIButton) {
searchEvent.isEnabled = true
}
func updateSearchResults(for searchController: UISearchController) {
print(searchController.searchBar.text)
}
But this code shows navigation bar,but when clicked on cancel button, the pressButton event isn't getting triggered?why is that?
You can simply set hidesNavigationBarDuringPresentation property to true which manages to hide Navigation Bar and show a search bar in that area.

How to prevent navbar hiding when keyboard appears ios?

I have an app which has a webview on a storyboard, which loads a registration form in ASP, so when the keyboard or a picker appears, the navbar hides, but never comes back, so I'm not able to go back to the last page.
Here's my code:
import UIKit
class BannerViewController: UIViewController {
#IBOutlet var webView11: UIWebView!
override var preferredStatusBarStyle: UIStatusBarStyle {
return .default
}
override var prefersStatusBarHidden: Bool {
return false
}
override func viewWillAppear(_ animated: Bool) {
self.navigationItem.setHidesBackButton(false, animated: true)
self.setNeedsStatusBarAppearanceUpdate()
self.navigationController?.navigationBar.barTintColor = UIColor(red: 0, green: 0.38, blue: 0.667, alpha: 1)
self.navigationController?.navigationBar.tintColor = UIColor(red: 1, green: 1, blue: 1, alpha: 1)
var titleTextAttributes = [String : AnyObject]();
titleTextAttributes[NSFontAttributeName] = UIFont.systemFont(ofSize: 17, weight: UIFontWeightSemibold)
titleTextAttributes[NSForegroundColorAttributeName] = UIColor(red: 1, green: 1, blue: 1, alpha: 1)
self.navigationController?.navigationBar.titleTextAttributes = titleTextAttributes
}
override func viewDidLoad() {
super.viewDidLoad();
self.navigationController?.navigationBar.barStyle = .default
self.setNeedsStatusBarAppearanceUpdate()
self.navigationController?.setNavigationBarHidden(false, animated: true)
self.webView11.delegate = self;
if let unwrappedwebView11 = URL(string: "http://www.stackoverflow") {
self.webView11.loadRequest(URLRequest(url: unwrappedwebView11))
}
}
}
extension BannerViewController: UIWebViewDelegate {
}
So, it looks like i need to refresh the view or something, as i added a back button out of the webView and when i click and go back to last view, and then i got in again to the webView the navBar doesn´t move anymore