navigation bar button text disappears - swift

I have 2 nav bar buttons at the top of the screen, one with an image an another with text like so:
When I present a new view controller with a new nav controller/root controller and then dismiss it, going back to the same view controller, the text disappears like so:
Here's the code for the view controller
override func viewDidLoad() {
setupNavBarButtons()
}
func setupNavBarButtons() {
let searchImage = UIImage(named: "search_icon")?.imageWithRenderingMode(.AlwaysOriginal)
let searchBarButtonItem = UIBarButtonItem(image: searchImage, style: .Plain, target: self, action: #selector(handleSearch))
navigationItem.rightBarButtonItem = searchBarButtonItem
let filterBarButtonItem = UIBarButtonItem(title: "Filter", style: .Plain , target: self, action: #selector(displayFilter))
navigationItem.leftBarButtonItem = filterBarButtonItem
}
func presentAccountController() {
let accountController = AccountController()
accountController.listController = self
let vc = UINavigationController(rootViewController: accountController
presentViewController(vc, animated: true, completion: nil)
}
The controller I'm dismissing to return to the original view controller:
class AccountController: UIViewController, UITableViewDelegate, UITableViewDataSource, MFMailComposeViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
view.backgroundColor = UIColor.rgb(245, green: 245, blue: 245)
let closeButtonItem = UIBarButtonItem(title: "Close", style: .Plain, target: self, action: #selector(dismissController))
navigationItem.leftBarButtonItem = closeButtonItem
}
func dismissController() {
self.dismissViewControllerAnimated(true, completion: nil)
}
I've tried calling it in viewWillLoad, viewDidLoad, viewDidAppear. Still no change and the bug still occurs.

You can do this and create a frame with a wide width when you create the button
lazy var filterButton: UIButton = {
let button = UIButton(type: .system)
button.frame = CGRect(x: 0, y: 0, width: 60, height: 30) // Set the width here to make it wider
button.setTitle("Filter", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 18)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(filterButtonTapped), for: .touchUpInside)
return button
}()
var leftBarButtonItem: UIBarButtonItem?
override func viewDidLoad() {
super.viewDidLoad()
leftBarButtonItem = UIBarButtonItem(customView: filterButton)
navigationItem.leftBarButtonItem = leftBarButtonItem
}
#objc func filterButtonTapped() {
print("Filter button tapped")
}

Related

InputAccessoryView and my problem (Swift)

I have such screen:
When you click on a textfield, the top of the keyboard should have "two arrows" on the left and the button "Done" on the right.
Like this:
I must say right away that I used Iqkeyboardmanager and because of this library there were problems therefore I had to remove it from the project. So help do it manually.
In the project, I already implemented the Done button, my code:
extension UITextField {
func addInputAccessoryView(title: String, target: Any, selector: Selector) {
let toolBar = UIToolbar(frame: CGRect(x: 0.0,
y: 0.0,
width: UIScreen.main.bounds.size.width,
height: 44.0))
let flexible = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let barButton = UIBarButtonItem(title: title, style: .plain, target: target, action: selector)
toolBar.setItems([flexible, barButton], animated: false)
self.inputAccessoryView = toolBar
}
}
final class ProfileSettingsViewController: UIViewController,UINavigationControllerDelegate, UIImagePickerControllerDelegate, UITextFieldDelegate {
#objc func tapDone() {
self.view.endEditing(true)
}
override func viewDidLoad() {
super.viewDidLoad()
self.nameTF.addInputAccessoryView(title: "Done", target: self, selector: #selector(tapDone))
self.companyTF.addInputAccessoryView(title: "Done", target: self, selector: #selector(tapDone))
self.nameTF.delegate = self
self.companyTF.delegate = self
.....
The rest of the code
}
}
What i have at the moment:
I need to add two black arrows on the left and make the Done buttons black. Help me please.
If you think my code is bad, you can provide your correct example. I am new in ios development.
1.Create a custom delegate which will notify the ViewController when some button is pressed inside the Input Accessory View.
protocol MyCustomTextFieldDelegate: class {
func doneButtonPressed()
func arrowDownPressed()
func arrowUpPressed()
}
2.Create a custom subclass of the the UITextField and make a function that will create the Input Accessory View. Define the newly created MyCustomTextFieldDelegate as a weak class property. (Note: we are making this subclass, as we are unable to add the MyCustomTextFieldDelegate as a property to the UITextField directly)
class MyCustomTextField: UITextField {
weak var customTextFieldDelegate: MyCustomTextFieldDelegate?
func enableInputAccessoryView() {
let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
let space = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(title: "Done", style: .done, target: self,
action: #selector(textFieldDonePressed))
doneButton.tintColor = .black
let arrowDown = UIBarButtonItem(image: UIImage(named: "arrow-right"), style: .done, target: self, action: #selector(arrowUpPressed))
arrowDown.tintColor = .black
let arrowUp = UIBarButtonItem(image: UIImage(named: "arrow-right"), style: .done, target: self, action: #selector(arrowDownPressed))
arrowUp.tintColor = .black
toolBar.setItems([arrowUp, arrowDown, space, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
toolBar.sizeToFit()
inputAccessoryView = toolBar
}
#objc private func textFieldDonePressed() {
endEditing(true)
customTextFieldDelegate?.doneButtonPressed()
}
#objc private func arrowDownPressed() {
customTextFieldDelegate?.arrowDownPressed()
}
#objc private func arrowUpPressed() {
customTextFieldDelegate?.arrowUpPressed()
}
}
3.In your ViewController change your current UITextField to your new MyCustomTextField and apply the Input Accessory View.
yourTextField.enableInputAccessoryView()
4.Set your ViewController to be the MyCustomTextField delegate.
yourTextField.customTextFieldDelegate = self
5.Confrom your ViewController to the newly created MyCustomTextFieldDelegate, where you will handle the user interaction.
extension YourViewController: MyCustomTextFieldDelegate {
func doneButtonPressed() {
// Handle done Pressed
}
func arrowDownPressed() {
// Handle down pressed
}
func arrowUpPressed() {
// Handle up pressed
}
}

how to use parameter inside the selector Tapgesture

Currently I am making pickerDate to interact with TextField.
class ViewController: UIViewController {
#IBOutlet weak var fromDateFilterTextField: UITextField!
#IBOutlet weak var toDateFilterTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
private func setupUI(){
fromDateFilterTextField.setInputViewDatePicker(target: self, selector: #selector(tapDone(_:)))
toDateFilterTextField.setInputViewDatePicker(target: self, selector: #selector(tapDone(_:)))
}
#objc func tapDone(textField : UITextField) {
}
}
extension UITextField{
func setInputViewDatePicker(target : Any , selector : Selector){
//create a UIDatePicker object and assign to inputView
let screenWidth = UIScreen.main.bounds.width
let datePicker = UIDatePicker(frame: CGRect(x: 0, y: 0, width: screenWidth, height: 200))
datePicker.datePickerMode = .date
self.inputView = datePicker
//create a toolbar and assign it to inputAccesoryView
let toolBar = UIToolbar(frame: CGRect(x: 0.0, y: 0.0, width: screenWidth, height: 44.0))
toolBar.barTintColor = .red
datePicker.setValue(UIColor.black, forKeyPath: "textColor")
datePicker.setValue(false, forKeyPath: "highlightsToday")
datePicker.backgroundColor = .white
let flexible = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let cancel = UIBarButtonItem(title: "Cancel", style: .plain, target: nil, action: #selector(tapCancel))
cancel.tintColor = .white
let barButton = UIBarButtonItem(title: "Done", style: .plain, target: target, action: selector) //7
barButton.tintColor = .white
toolBar.setItems([cancel, flexible, barButton], animated: false)
self.inputAccessoryView = toolBar
}
#objc func tapCancel() {
self.resignFirstResponder()
}
}
I'd like to pass textfield as parameter of tapDone for reuse the code, because I'd like to update the date of differents textfield using only change the parameters of tapDone.
OK, I understand (generally) what you are trying to do. The short answer is that you can't pass your UITextField as an argument for the Selector executed by your UIBarButtonItem. That said, there are other options.
The simplest solution I can think of would be to use the tag attribute of your UITextFields and UIBarButtonItems. To set this up you would need to update the tag of each UITextField so that they are all unique. For example, your fromDateFilterTextField could have a tag of 0, the toDateFilterTextField could be 1. Just make sure they are all different from each other if you have more.
Once you have updated the tags on your textFields you can make sure the UIBarButtonItem has the same tag by modifying your setInputViewDatePicker method like so:
func setInputViewDatePicker(target : Any , selector : Selector){
...
let barButton = UIBarButtonItem(title: "Done", style: .plain, target: target, action: selector) //7
barButton.tintColor = .white
//Make the barButton tag match the associated textField's tag
barButton.tag = self.tag
...
}
Even though you can't pass the UITextField as an argument, you can pass the UIBarButton executing the Selector by changing your selector method like this:
#objc func tapDone(_ sender: UIBarButtonItem) {
}
Then you can determine which textField is associated with the tapDone by looking for that matching tag:
#objc func tapDone(_ sender: UIBarButtonItem) {
switch sender.tag {
case 0:
print("This is fromDateFilterTextField")
case 1:
print("This is toDateFilterTextField")
default:
print("This is a different textField")
}
}
You can also look into viewWithTag in the docs for possible improvements, though sometimes being really explicit makes your code more readable and less prone to bugs.

Can't dismiss KeyBoard when a Done button pressed in UITableViewCell

First, done button codes are below
class ViewController: UIViewController, UITextFieldDelegate {
let inputNumber = UITextField(frame: CGRect(x: 150.0, y: 100.0, width: 200.0, height: 50.0))
let toolBarKeyBoard = UIToolbar()
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: #selector(donePressed))
var result : String!
override func viewDidLoad() {
super.viewDidLoad()
calculatePrice()
}
func calculatePrice () {
priceInputLabel.keyboardType = .numberPad
priceInputLabel.clearButtonMode = .whileEditing
self.view.addSubview(priceInputLabel)
toolBarKeyBoard.sizeToFit()
toolBarKeyBoard.setItems([flexibleSpace, doneButton], animated: false)
priceInputLabel.inputAccessoryView = toolBarKeyBoard
}
#objc func donePressed() {
view.endEditing(true)
}
}
It works OK. When I touch 'inputNumber(UITextField)', a keyboard pops up. And when I input Number and touch 'Done' button, a keyboard dismisses. Good.
But, in other codes, down below, doesn't work.
class FruitTableViewCell: UITableViewCell, UITextFieldDelegate {
var fruitsTextField = UITextField()
let toolBarKeyBoard = UIToolbar()
let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: #selector(donePressed))
var result : String!
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(fruitsTextField)
}
override func layoutSubviews() {
super.layoutSubviews()
fruitsTextField.frame = CGRect(x: 250, y: 7.5, width: 100, height: 30)
fruitsTextField.textColor = UIColor(red: CGFloat(242/255.0), green: CGFloat(56/255.0), blue: CGFloat(90/255.0), alpha: 1.0)
fruitsTextField.keyboardType = .numberPad
fruitsTextField.clearButtonMode = .whileEditing
toolBarKeyBoard.sizeToFit()
fruitsTextField.inputAccessoryView = toolBarKeyBoard
toolBarKeyBoard.setItems([flexibleSpace, doneButton], animated: false)
}
#objc func donePressed() {
fruitTextField.endEditing(true)
}
I can build, I can toggle a keyboard, I can touch a done button, but it doesn't dismiss a keyboard.
I think, the function '#objc func donePressed()' at the bottom line is matter.
First codes are 'view.endEditing(true)' but these are 'fruitTextField.endEditing(true)'
So, I tried to change codes.
#objc func donePressed() {
contentView.endEditing(true)
}
But doesn't work.
Question1. How can I dismiss a keyboard?
Question2. Why doesn't a keyboard dismiss even though I touched 'Done' button?
Question3. In second code, is a keyboard not the FirstResponder?
Question4. In second code, what is the View for '.endEditing'?
Thanks!
Change your "done button" initialization to:
lazy var doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(donePressed))
You need target: self, and you need it to be lazy in order for self to be valid when the button is instantiated.
You can also change your done func to:
#objc func donePressed() {
fruitsTextField.resignFirstResponder()
}
Doesn't really change the functionality, but I believe it is the recommended method.

Add action to UIBarButtonItem dynamically Swift 4

let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(buttonClicked(sender:)))
#objc func buttonClicked(sender: UIBarButtonItem) {
print("Hello")
}
That's the code for my UIBarButton but when I click on it it doesn't print "Hello", what could be the problem?
EDIT: Here are my viewcontroller
It simply control a button that when it's clicked show the spinner with its control, but as I said before the button on toolbar doesn't work
class FilterViewController: UIViewController {
var search: Search?
let categoriesSpinnerDelegate = CategoriesPickerDelegate()
#IBOutlet weak var generalSpinner: UIPickerView!
#IBOutlet weak var categoriesButton: UIButton!
#IBOutlet weak var categoryRow: UIView!
var doneButton: UIBarButtonItem?
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.topViewController?.title = "Filtri"
// Set border and click action
self.categoryRow.layer.borderWidth = 1
self.categoryRow.layer.borderColor = Raccoltacase.lightGray.cgColor
self.categoryRow.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.buttonClicked(sender:))))
// Create toolbar and attach it to pickerView
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
//toolBar.tintColor = UIColor(red: 76/255, green: 217/255, blue: 100/255, alpha: 1)
toolBar.sizeToFit()
self.doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(buttonClicked(sender:)))
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.plain, target: self, action: nil)
toolBar.setItems([cancelButton, spaceButton, doneButton!], animated: false)
toolBar.isUserInteractionEnabled = true
generalSpinner.addSubview(toolBar)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func categoriesButtonClick(_ sender: UIButton) {
self.generalSpinner.showsSelectionIndicator = true
self.generalSpinner.dataSource = categoriesSpinnerDelegate
self.generalSpinner.delegate = categoriesSpinnerDelegate
self.generalSpinner.isHidden = false
}
#objc func buttonClicked(sender: UIBarButtonItem) {
print("Hello")
}
}
Screen
Assuming that you are showing the picker sometimes (and dismissing it when user presses the done button), here is my solution:
Add a UITextField to the view in storyboard (and make the tintColor transparent (clearColor))
Add the UIToolbar as inputAccessoryView to the UITextField
Add the UIPickerView as inputView to the toolbar (Also see the note below)
Below is a sample code:
override func viewDidLoad() {
super.viewDidLoad()
textField.inputView = generalSpinner
textField.inputAccessoryView = getToolbar()
}
func getToolbar() -> UIToolbar {
let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 40))
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
self.doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(buttonClicked(sender:)))
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.plain, target: self, action: nil)
toolBar.setItems([cancelButton, spaceButton, doneButton!], animated: false)
toolBar.isUserInteractionEnabled = true
return toolBar
}
Note: In case the UIPickerView is already in the storyboard (or a subview of another view), make sure to remove it in the first line of viewDidLoad (as shown below):
override func viewDidLoad() {
super.viewDidLoad()
generalSpinner.removeFromSuperview()
textField.inputView = generalSpinner
textField.inputAccessoryView = getToolbar()
}
SOLVED
When i use pickerView.addSubView(toolbar) the toolbar was placed behind the pickerview and so it was no clickable. I solved it adding the toolbar manually from storyboard with General Spinner.top = Picker Toolbar.bottom

Swift custom back button in navigation bar

I am trying to use a custom image for my back button in the navigation bar. I am using the below code, which adds the image, but also keeps the text "Back" in the button. I want to also remove the text. Can I do that?
self.navigationController?.navigationBar.backIndicatorImage = UIImage(named: "icon-back")
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = UIImage(named: "icon-back")
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
Try code below :-)
func SetBackBarButtonCustom()
{
//Back buttion
let btnLeftMenu: UIButton = UIButton()
btnLeftMenu.setImage(UIImage(named: "back_arrow"), for: UIControlState())
btnLeftMenu.addTarget(self, action: #selector(UIViewController.onClcikBack), for: UIControlEvents.touchUpInside)
btnLeftMenu.frame = CGRect(x: 0, y: 0, width: 33/2, height: 27/2)
let barButton = UIBarButtonItem(customView: btnLeftMenu)
self.navigationItem.leftBarButtonItem = barButton
}
func onClcikBack()
{
_ = self.navigationController?.popViewController(animated: true)
}
If you want to add Back button in every UIViewController then you can add the code in UIViewController extension else you can use addBackButton() directly as follows.
extension UIViewController {
func addBackButton() {
let btnLeftMenu: UIButton = UIButton()
let image = UIImage(named: "backButtonImage");
btnLeftMenu.setImage(image, for: .normal)
btnLeftMenu.setTitle("Back".localized, for: .normal);
btnLeftMenu.sizeToFit()
btnLeftMenu.addTarget(self, action: #selector (backButtonClick(sender:)), for: .touchUpInside)
let barButton = UIBarButtonItem(customView: btnLeftMenu)
self.navigationItem.leftBarButtonItem = barButton
}
func backButtonClick(sender : UIButton) {
self.navigationController?.popViewController(animated: true);
}
}
Make sure you should add the following file "backButtonImage.png" in your app bundle.
Call this method self.addBackButton()in your viewDidLoad method of your custom UIViewController class like this
override func viewDidLoad() {
super.viewDidLoad()
self.addBackButton()
}
Note : if you don't add addBackButton Method in extension then you will need to add this method directly in the class and set target and Selector accordingly.