NavBar Button doesn't work when being clicked - swift

I'm trying to implement a button to my navbar which should call the function addTap.
Unfortunately though, when I click the add button in the simulator, nothing happens. I put a print statement in the addTap function but I doesn't get called, meaning the function never gets called.
Have any ideas?
class ViewController: UIViewController {
var names : [String] = []
let TableView = UITableView() // view
var addButton: UIBarButtonItem = UIBarButtonItem(title: "Add", style: .done, target: self, action: #selector(addTap))
override func loadView() {
super.loadView()
setupComponents()
setupTableView()
}
func setupComponents() {
self.title = "Core Data"
self.navigationItem.rightBarButtonItem = self.addButton
}
func setupTableView() {
view.addSubview(TableView)
TableView.translatesAutoresizingMaskIntoConstraints = false
TableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
TableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
TableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
TableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
}
#objc func addTap() {
print("reached")
let alert = UIAlertController(title: "New Friend", message: "Add the name of your friend", preferredStyle: .alert)
let saveAction = UIAlertAction(title: "Add Now", style: .default) { [unowned self] action in
guard let textField = alert.textFields?.first, let nameToSave = textField.text else { return }
self.names.append(nameToSave)
self.TableView.reloadData()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
alert.addTextField()
alert.addAction(saveAction)
alert.addAction(cancelAction)
present(alert, animated: true)
}
}

Your issue is with the construction of your UIBarButtonItem. You are trying to access self before self exists.
If you construct it like this you will find that it works.
func setupComponents() {
self.title = "Core Data"
let addButton: UIBarButtonItem = UIBarButtonItem(title: "Add", style: .done, target: self, action: #selector(addTap))
self.navigationItem.rightBarButtonItem = addButton
}
Or if you need access to the button instance you can create the UIBarButtonItem lazily and replace the declaration in your class with the following:
lazy var addButton: UIBarButtonItem = {
UIBarButtonItem(title: "Add", style: .done, target: self, action: #selector(addTap))
}()

Related

How to set textfield placeholder's value to time picker's selected value

On "func can()" pop up shows a textfield on which we click & a time picker shows.I want to set textfield placeholder value to that selected picker's value
#IBAction func can(_ sender: UIButton) {
let alertController : UIAlertController = UIAlertController(title: "", message: "Confirm Order ?", preferredStyle: UIAlertController.Style.alert)
alertController.addTextField
{
(textField : UITextField!) -> Void in
textField.placeholder = "Expected Time"
textField.addTarget(self, action: #selector(self.myTargetFunction), for: .touchDown)
let okAction = UIAlertAction(title: "CANCEL", style: UIAlertAction.Style.destructive) {
(UIAlertAction) in
}
let CANAction = UIAlertAction(title: "SUBMIT", style: UIAlertAction.Style.default) {
(UIAlertAction) in
// self.actionCancelOrderApiCall()
}
alertController.addAction(okAction)
alertController.addAction(CANAction)
self.present(alertController, animated: true, completion: nil)
}
}
#objc func myTargetFunction(textField: UITextField) {
var datePicker : UIDatePicker = UIDatePicker(frame: CGRect(x: 0,y: 500, width: self.view.frame.size.width, height: 250))
datePicker.datePickerMode = .time
datePicker.backgroundColor = UIColor.white
if datePicker.isHidden == false{
tblV.isHidden = true
atchment1.isHidden = true
}
self.view.addSubview(datePicker)
}
The below changes should make it work
textField.addTarget(self, action: #selector(self.myTargetFunction(sender:)), for: .touchDown)
change your method signature to
#objc func myTargetFunction(sender: UITextField)
inside myTargetFunction(),
sender.placeholderText = datePicker.date

UIBarButtonItem selector not working

I have a MainViewController embed in a Navigation Controller, as shown below:
And in MainViewController.swift, I added two UIBarButtonItem(left and right) programmatically:
class MainViewController: UIViewController {
let rightButton = UIBarButtonItem(title: "Right", style: .plain, target: self, action: #selector(onRightClick))
let leftButton = UIBarButtonItem(title: "Left", style: .plain, target: self, action: #selector(onLeftClick))
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.rightBarButtonItem = rightButton
navigationItem.leftBarButtonItem = leftButton
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#objc func onRightClick() {
print("[Main] Right Click")
}
#objc func onLeftClick() {
print("[Main] Left Click")
}
}
The buttons did show on the screen, but the interesting thing is, the selector functions onLeftClick and onRightClick never get called whenever I pressed left or right button. Is there anything I should do to make it works? I am using Xcode 9.3.
try with inside scope once
override func viewDidLoad() {
super.viewDidLoad()
let rightButton = UIBarButtonItem(title: "Right", style: .plain, target: self, action: #selector(self.onRightLeftClick(_ :)))
rightButton.tag = 1
let leftButton = UIBarButtonItem(title: "Left", style: .plain, target: self, action: #selector(self.onRightLeftClick(_ :)))
rightButton.tag = 2
self.navigationItem.rightBarButtonItem = rightButton
self.navigationItem.leftBarButtonItem = leftButton
}
handle the action as
func onRightLeftClick(_ sender: UIBarButtonItem){
if sender.tag == 1{
// rightButton action
}else{
// leftButton action
}
}
You can also just add the lazy keyword to the rightButton and leftButton class properties. That way, the UIBarButtonItem won't be instantiated (and the action selectors won't attempt to be resolved) until they are used inside the class. Doing it this way allows you to use the barButtonItems anywhere in the class (not just in the function they are declared in).
lazy var rightButton = UIBarButtonItem(title: "Right", style: .plain, target: self, action: #selector(onRightClick))
lazy var leftButton = UIBarButtonItem(title: "Left", style: .plain, target: self, action: #selector(onLeftClick))

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

Connecting a custom PickerView to a textfield

I got a custom UIPickerView from github (https://github.com/bendodson/MonthYearPickerView-Swift") and now I'm trying to connect it to a textfield with no luck. I managed to do it with the standard UiDatePicker using .addTarget and .valueChanged methods, but with this custom one addTarget throws an error. Now I only manage to get the textfield's inputView to the custom PickerView, but not save the input using my "Done" button that I created. What is it that I'm missing?
lazy var ExpireDatetextfeild: UITextField = {
let tf = LeftPaddedTextFeild()
tf.placeholder = "MM/YY"
tf.translatesAutoresizingMaskIntoConstraints = false
tf.addTarget(self, action: #selector(textfeildediting), for: .editingDidBegin)
return tf
}()
let DatePickerView: MonthYearPickerView = MonthYearPickerView()
func textfeildediting() {
let DatePickerView: MonthYearPickerView = MonthYearPickerView()
DatePickerView.backgroundColor = .white
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.default
toolBar.isTranslucent = true
toolBar.backgroundColor = UIColor.white
toolBar.sizeToFit()
let donebutton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(DoneFunc))
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.plain, target: self, action: #selector(cancelFunc))
toolBar.setItems([cancelButton, spaceButton, donebutton], animated: false)
toolBar.isUserInteractionEnabled = true
ExpireDatetextfeild.inputAccessoryView = toolBar
ExpireDatetextfeild.inputView = DatePickerView
}
func cancelFunc(sender: UIBarButtonItem) {
DatePickerView.isHidden = true
print("DatePickerPrint")
}
func DoneFunc(sender: UIBarButtonItem) {
DatePickerView.onDateSelected = { (month: Int, year:Int) in
let Yearstring = String(format: "%02d/%d", month, year)
print(Yearstring)
}
}

Set table view into editing mode

I have a UITableView in a UIViewController and have added an edit button from code rather than IB. This comes with UITableViewControllers but not UIVCs. How can I get this button to put the table view into editing mode in swift? Thanks in advance for any help.
class WordsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
}
Here is a solution for Swift 4.2:
override func viewDidLoad() {
super.viewDidLoad()
// Use the edit button provided by the view controller.
navigationItem.rightBarButtonItem = editButtonItem
}
override func setEditing(_ editing: Bool, animated: Bool) {
// Takes care of toggling the button's title.
super.setEditing(editing, animated: true)
// Toggle table view editing.
tableView.setEditing(editing, animated: true)
}
The view controller's setEditing is called by default when the editButtonItem is pressed. By default, pressing the button toggles its title between "Edit" and "Done", so calling super.setEditing takes care of that for us, and we use the tableView's setEditing method to toggle the editing state of the table view.
Sources:
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621471-editbuttonitem
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621378-setediting
https://developer.apple.com/documentation/uikit/uitableview/1614876-setediting
Create rightBarButtonItem as below with an action.
In viewDidLoad() :
let rightButton = UIBarButtonItem(title: "Edit", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("showEditing:"))
self.navigationItem.rightBarButtonItem = rightButton
and then make a function like,
func showEditing(sender: UIBarButtonItem)
{
if(self.tableView.isEditing == true)
{
self.tableView.isEditing = false
self.navigationItem.rightBarButtonItem?.title = "Done"
}
else
{
self.tableView.isEditing = true
self.navigationItem.rightBarButtonItem?.title = "Edit"
}
}
Make sure, : is appended to function name in Selector of action in viewDidLoad
Hope it helps!
Swift 3 & 4 answer that IMHO is better than other answers:
override func viewDidLoad() {
super.viewDidLoad()
let editButton = UIBarButtonItem(title: "Edit", style: .plain, target: self, action: #selector(toggleEditing)) // create a bat button
navigationItem.rightBarButtonItem = editButton // assign button
}
#objc private func toggleEditing() {
listTableView.setEditing(!listTableView.isEditing, animated: true) // Set opposite value of current editing status
navigationItem.rightBarButtonItem?.title = listTableView.isEditing ? "Done" : "Edit" // Set title depending on the editing status
}
Why do I think it's better:
Fewer code lines.
Bar button is initialized once but not every time you press the button.
Call this method on button click.
tableView.setEditing(true, animated: true)
Or if you want it to work like a toggle use
tableView.setEditing(!tableView.editing, animated: true)
I assume you have a button, which calls editButtonPressed on press. So implementation of this method could look like this.
override func viewDidLoad(){
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Edit", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("editButtonPressed"))
}
func editButtonPressed(){
tableView.setEditing(!tableView.editing, animated: true)
if tableView.editing == true{
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("editButtonPressed"))
}else{
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Edit", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("editButtonPressed"))
}
}
This also changes title of the bar button.
Override the view controller's -setEditing:animated:, call super, and call the same method on your table view.
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
[self.tableView setEditing:editing animated:animated];
}
First :
let editButton = UIBarButtonItem(title: "Edit", style: .plain, target: self, action: #selector(showEditing(_:)))
navigationItem.rightBarButtonItem = editButton
Then :
#objc func showEditing(_ sender: UIBarButtonItem)
{
if(self.tableView.isEditing == true)
{
self.tableView.isEditing = false
self.navigationItem.rightBarButtonItem?.title = "Edit"
}
else
{
self.tableView.isEditing = true
self.navigationItem.rightBarButtonItem?.title = "Done"
}
}
Swift 3.0 version of njuri post:
override func viewDidLoad() {
super.viewDidLoad()
PackageNameLabel.text = detailPackageName
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Edit", style: UIBarButtonItemStyle.plain, target: self, action: #selector(PackageDetailsTableViewController.editButtonPressed))
}
func editButtonPressed(){
tableView.setEditing(!tableView.isEditing, animated: true)
if tableView.isEditing == true{
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(PackageDetailsTableViewController.editButtonPressed))
}else{
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Edit", style: UIBarButtonItemStyle.plain, target: self, action: #selector(PackageDetailsTableViewController.editButtonPressed))
}
}
You only need 1 line of code in viewDidLoad() to get edit button and its related functionality.
navigationItem.rightBarButtonItem = editButtonItem